- Rewrite Wave API to enumerate wave out / in devices at startup
[reactos.git] / reactos / drivers / wdm / audio / legacy / wdmaud / mixer.c
1 /*
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
7 * Johannes Anderwald
8 */
9 #include "wdmaud.h"
10
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}};
24 const GUID KSNODETYPE_SUM = {0xDA441A60L, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
25
26 #define DESTINATION_LINE 0xFFFF0000
27
28 LPMIXERLINE_EXT
29 GetSourceMixerLine(
30 LPMIXER_INFO MixerInfo,
31 DWORD dwSource)
32 {
33 PLIST_ENTRY Entry;
34 LPMIXERLINE_EXT MixerLineSrc;
35
36 /* get first entry */
37 Entry = MixerInfo->LineList.Flink;
38
39 while(Entry != &MixerInfo->LineList)
40 {
41 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
42 DPRINT("dwSource %x dwSource %x\n", MixerLineSrc->Line.dwSource, dwSource);
43 if (MixerLineSrc->Line.dwSource == dwSource)
44 return MixerLineSrc;
45
46 Entry = Entry->Flink;
47 }
48
49 return NULL;
50 }
51
52 LPMIXERLINE_EXT
53 GetSourceMixerLineByLineId(
54 LPMIXER_INFO MixerInfo,
55 DWORD dwLineID)
56 {
57 PLIST_ENTRY Entry;
58 LPMIXERLINE_EXT MixerLineSrc;
59
60 /* get first entry */
61 Entry = MixerInfo->LineList.Flink;
62
63 while(Entry != &MixerInfo->LineList)
64 {
65 MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
66 DPRINT1("dwLineID %x dwLineID %x\n", MixerLineSrc->Line.dwLineID, dwLineID);
67 if (MixerLineSrc->Line.dwLineID == dwLineID)
68 return MixerLineSrc;
69
70 Entry = Entry->Flink;
71 }
72
73 return NULL;
74 }
75
76
77
78 ULONG
79 GetPinCount(
80 IN PFILE_OBJECT FileObject)
81 {
82 KSPROPERTY Pin;
83 NTSTATUS Status;
84 ULONG NumPins, BytesReturned;
85
86 Pin.Flags = KSPROPERTY_TYPE_GET;
87 Pin.Set = KSPROPSETID_Pin;
88 Pin.Id = KSPROPERTY_PIN_CTYPES;
89
90 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSPROPERTY), (PVOID)&NumPins, sizeof(ULONG), &BytesReturned);
91 if (!NT_SUCCESS(Status))
92 return 0;
93
94 return NumPins;
95 }
96
97
98 ULONG
99 GetSysAudioDeviceCount(
100 IN PDEVICE_OBJECT DeviceObject)
101 {
102 PWDMAUD_DEVICE_EXTENSION DeviceExtension;
103 KSPROPERTY Pin;
104 ULONG Count, BytesReturned;
105 NTSTATUS Status;
106
107 /* setup the query request */
108 Pin.Set = KSPROPSETID_Sysaudio;
109 Pin.Id = KSPROPERTY_SYSAUDIO_DEVICE_COUNT;
110 Pin.Flags = KSPROPERTY_TYPE_GET;
111
112 DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
113
114 /* query sysaudio for the device count */
115 Status = KsSynchronousIoControlDevice(DeviceExtension->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSPROPERTY), (PVOID)&Count, sizeof(ULONG), &BytesReturned);
116 if (!NT_SUCCESS(Status))
117 return 0;
118
119 return Count;
120 }
121
122 NTSTATUS
123 GetSysAudioDevicePnpName(
124 IN PDEVICE_OBJECT DeviceObject,
125 IN ULONG DeviceIndex,
126 OUT LPWSTR * Device)
127 {
128 ULONG BytesReturned;
129 KSP_PIN Pin;
130 NTSTATUS Status;
131 PWDMAUD_DEVICE_EXTENSION DeviceExtension;
132
133 /* first check if the device index is within bounds */
134 if (DeviceIndex >= GetSysAudioDeviceCount(DeviceObject))
135 return STATUS_INVALID_PARAMETER;
136
137 /* setup the query request */
138 Pin.Property.Set = KSPROPSETID_Sysaudio;
139 Pin.Property.Id = KSPROPERTY_SYSAUDIO_DEVICE_INTERFACE_NAME;
140 Pin.Property.Flags = KSPROPERTY_TYPE_GET;
141 Pin.PinId = DeviceIndex;
142
143 DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
144
145 /* query sysaudio for the device path */
146 Status = KsSynchronousIoControlDevice(DeviceExtension->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSPROPERTY) + sizeof(ULONG), NULL, 0, &BytesReturned);
147
148 /* check if the request failed */
149 if (Status != STATUS_BUFFER_TOO_SMALL || BytesReturned == 0)
150 return STATUS_UNSUCCESSFUL;
151
152 /* allocate buffer for the device */
153 *Device = ExAllocatePool(NonPagedPool, BytesReturned);
154 if (!Device)
155 return STATUS_INSUFFICIENT_RESOURCES;
156
157 /* query sysaudio again for the device path */
158 Status = KsSynchronousIoControlDevice(DeviceExtension->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSPROPERTY) + sizeof(ULONG), (PVOID)*Device, BytesReturned, &BytesReturned);
159
160 if (!NT_SUCCESS(Status))
161 {
162 /* failed */
163 ExFreePool(*Device);
164 return Status;
165 }
166
167 return Status;
168 }
169
170 ULONG
171 GetDeviceIndexFromPnpName(
172 IN PDEVICE_OBJECT DeviceObject,
173 LPWSTR Device)
174 {
175 ULONG Count, Index;
176 LPWSTR DeviceName;
177 NTSTATUS Status;
178
179 /* get device count */
180 Count = GetSysAudioDeviceCount(DeviceObject);
181
182 if (!Count)
183 return MAXULONG;
184
185 for(Index = 0; Index < Count; Index++)
186 {
187 /* get device name */
188 Status = GetSysAudioDevicePnpName(DeviceObject, Index, &DeviceName);
189 if (NT_SUCCESS(Status))
190 {
191 if (!wcsicmp(Device, DeviceName))
192 {
193 /* found device index */
194 ExFreePool(DeviceName);
195 return Index;
196 }
197 ExFreePool(DeviceName);
198 }
199 }
200
201 return MAXULONG;
202 }
203
204
205 NTSTATUS
206 OpenDevice(
207 IN LPWSTR Device,
208 OUT PHANDLE DeviceHandle,
209 OUT PFILE_OBJECT * FileObject)
210 {
211 NTSTATUS Status;
212 HANDLE hDevice;
213
214 /* now open the device */
215 Status = WdmAudOpenSysAudioDevice(Device, &hDevice);
216
217 if (!NT_SUCCESS(Status))
218 {
219 return Status;
220 }
221
222 *DeviceHandle = hDevice;
223
224 if (FileObject)
225 {
226 Status = ObReferenceObjectByHandle(hDevice, FILE_READ_DATA | FILE_WRITE_DATA, IoFileObjectType, KernelMode, (PVOID*)FileObject, NULL);
227
228 if (!NT_SUCCESS(Status))
229 {
230 ZwClose(hDevice);
231 }
232 }
233
234 return Status;
235
236 }
237
238
239 NTSTATUS
240 OpenSysAudioDeviceByIndex(
241 IN PDEVICE_OBJECT DeviceObject,
242 IN ULONG DeviceIndex,
243 IN PHANDLE DeviceHandle,
244 IN PFILE_OBJECT * FileObject)
245 {
246 LPWSTR Device = NULL;
247 NTSTATUS Status;
248
249 Status = GetSysAudioDevicePnpName(DeviceObject, DeviceIndex, &Device);
250 if (!NT_SUCCESS(Status))
251 return Status;
252
253 Status = OpenDevice(Device, DeviceHandle, FileObject);
254
255 /* free device buffer */
256 ExFreePool(Device);
257
258 return Status;
259 }
260
261 NTSTATUS
262 GetFilterNodeProperty(
263 IN PFILE_OBJECT FileObject,
264 IN ULONG PropertyId,
265 PKSMULTIPLE_ITEM * Item)
266 {
267 NTSTATUS Status;
268 ULONG BytesReturned;
269 PKSMULTIPLE_ITEM MultipleItem;
270 KSPROPERTY Property;
271
272 /* setup query request */
273 Property.Id = PropertyId;
274 Property.Flags = KSPROPERTY_TYPE_GET;
275 Property.Set = KSPROPSETID_Topology;
276
277 /* query for required size */
278 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), NULL, 0, &BytesReturned);
279
280 /* check for success */
281 if (Status != STATUS_MORE_ENTRIES)
282 return Status;
283
284 /* allocate buffer */
285 MultipleItem = (PKSMULTIPLE_ITEM)ExAllocatePool(NonPagedPool, BytesReturned);
286 if (!MultipleItem)
287 return STATUS_INSUFFICIENT_RESOURCES;
288
289 /* query for required size */
290 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), (PVOID)MultipleItem, BytesReturned, &BytesReturned);
291
292 if (!NT_SUCCESS(Status))
293 {
294 /* failed */
295 ExFreePool(MultipleItem);
296 return Status;
297 }
298
299 *Item = MultipleItem;
300 return Status;
301 }
302
303 ULONG
304 CountNodeType(
305 PKSMULTIPLE_ITEM MultipleItem,
306 LPGUID NodeType)
307 {
308 ULONG Count;
309 ULONG Index;
310 LPGUID Guid;
311
312 Count = 0;
313 Guid = (LPGUID)(MultipleItem+1);
314
315 /* iterate through node type array */
316 for(Index = 0; Index < MultipleItem->Count; Index++)
317 {
318 if (IsEqualGUIDAligned(NodeType, Guid))
319 {
320 /* found matching guid */
321 Count++;
322 }
323 Guid++;
324 }
325 return Count;
326 }
327
328 ULONG
329 GetNodeTypeIndex(
330 PKSMULTIPLE_ITEM MultipleItem,
331 LPGUID NodeType)
332 {
333 ULONG Index;
334 LPGUID Guid;
335
336 Guid = (LPGUID)(MultipleItem+1);
337
338 /* iterate through node type array */
339 for(Index = 0; Index < MultipleItem->Count; Index++)
340 {
341 if (IsEqualGUIDAligned(NodeType, Guid))
342 {
343 /* found matching guid */
344 return Index;
345 }
346 Guid++;
347 }
348 return MAXULONG;
349 }
350
351 ULONG
352 GetControlTypeFromTopologyNode(
353 IN LPGUID NodeType)
354 {
355 if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_AGC))
356 {
357 // automatic gain control
358 return MIXERCONTROL_CONTROLTYPE_ONOFF;
359 }
360 else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_LOUDNESS))
361 {
362 // loudness control
363 return MIXERCONTROL_CONTROLTYPE_LOUDNESS;
364 }
365 else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_MUTE ))
366 {
367 // mute control
368 return MIXERCONTROL_CONTROLTYPE_MUTE;
369 }
370 else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_TONE))
371 {
372 // tpne control
373 //FIXME
374 // MIXERCONTROL_CONTROLTYPE_ONOFF if KSPROPERTY_AUDIO_BASS_BOOST is supported
375 // MIXERCONTROL_CONTROLTYPE_BASS if KSPROPERTY_AUDIO_BASS is supported
376 // MIXERCONTROL_CONTROLTYPE_TREBLE if KSPROPERTY_AUDIO_TREBLE is supported
377 UNIMPLEMENTED;
378 return MIXERCONTROL_CONTROLTYPE_ONOFF;
379 }
380 else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_VOLUME))
381 {
382 // volume control
383 return MIXERCONTROL_CONTROLTYPE_VOLUME;
384 }
385 else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_PEAKMETER))
386 {
387 // peakmeter control
388 return MIXERCONTROL_CONTROLTYPE_PEAKMETER;
389 }
390 else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_MUX))
391 {
392 // mux control
393 return MIXERCONTROL_CONTROLTYPE_MUX;
394 }
395 else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_MUX))
396 {
397 // mux control
398 return MIXERCONTROL_CONTROLTYPE_MUX;
399 }
400 else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_STEREO_WIDE))
401 {
402 // stero wide control
403 return MIXERCONTROL_CONTROLTYPE_FADER;
404 }
405 else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_CHORUS))
406 {
407 // chorus control
408 return MIXERCONTROL_CONTROLTYPE_FADER;
409 }
410 else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_REVERB))
411 {
412 // reverb control
413 return MIXERCONTROL_CONTROLTYPE_FADER;
414 }
415 else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_SUPERMIX))
416 {
417 // supermix control
418 // MIXERCONTROL_CONTROLTYPE_MUTE if KSPROPERTY_AUDIO_MUTE is supported
419 UNIMPLEMENTED;
420 return MIXERCONTROL_CONTROLTYPE_VOLUME;
421 }
422 UNIMPLEMENTED
423 return 0;
424 }
425
426 NTSTATUS
427 GetPhysicalConnection(
428 IN PFILE_OBJECT FileObject,
429 IN ULONG PinId,
430 OUT PKSPIN_PHYSICALCONNECTION *OutConnection)
431 {
432 KSP_PIN Pin;
433 NTSTATUS Status;
434 ULONG BytesReturned;
435 PKSPIN_PHYSICALCONNECTION Connection;
436
437 /* setup the request */
438 Pin.Property.Flags = KSPROPERTY_TYPE_GET;
439 Pin.Property.Id = KSPROPERTY_PIN_PHYSICALCONNECTION;
440 Pin.Property.Set = KSPROPSETID_Pin;
441 Pin.PinId = PinId;
442
443 /* query the pin for the physical connection */
444 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
445
446 if (Status == STATUS_NOT_FOUND)
447 {
448 /* pin does not have a physical connection */
449 return Status;
450 }
451
452 Connection = ExAllocatePool(NonPagedPool, BytesReturned);
453 if (!Connection)
454 {
455 /* not enough memory */
456 return STATUS_INSUFFICIENT_RESOURCES;
457 }
458
459 /* query the pin for the physical connection */
460 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Connection, BytesReturned, &BytesReturned);
461 if (!NT_SUCCESS(Status))
462 {
463 /* failed to query the physical connection */
464 ExFreePool(Connection);
465 return Status;
466 }
467
468 /* store connection */
469 *OutConnection = Connection;
470 return Status;
471 }
472
473 NTSTATUS
474 GetNodeIndexes(
475 IN PKSMULTIPLE_ITEM MultipleItem,
476 IN ULONG NodeIndex,
477 IN ULONG bNode,
478 IN ULONG bFrom,
479 OUT PULONG NodeReferenceCount,
480 OUT PULONG *NodeReference)
481 {
482 ULONG Index, Count = 0;
483 PKSTOPOLOGY_CONNECTION Connection;
484 PULONG Refs;
485
486 /* KSMULTIPLE_ITEM is followed by several KSTOPOLOGY_CONNECTION */
487 Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
488
489 /* first count all referenced nodes */
490 for(Index = 0; Index < MultipleItem->Count; Index++)
491 {
492 //DbgPrint("FromPin %u FromNode %u ToPin %u ToNode %u\n", Connection->FromNodePin, Connection->FromNode, Connection->ToNodePin, Connection->ToNode);
493 if (bNode)
494 {
495 if (bFrom)
496 {
497 if (Connection->FromNode == NodeIndex)
498 {
499 /* node id has a connection */
500 Count++;
501 }
502 }
503 else
504 {
505 if (Connection->ToNode == NodeIndex)
506 {
507 /* node id has a connection */
508 Count++;
509 }
510 }
511 }
512 else
513 {
514 if (bFrom)
515 {
516 if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE)
517 {
518 /* node id has a connection */
519 Count++;
520 }
521 }
522 else
523 {
524 if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE)
525 {
526 /* node id has a connection */
527 Count++;
528 }
529 }
530 }
531
532
533 /* move to next connection */
534 Connection++;
535 }
536
537 ASSERT(Count != 0);
538
539 /* now allocate node index array */
540 Refs = ExAllocatePool(NonPagedPool, sizeof(ULONG) * Count);
541 if (!Refs)
542 {
543 /* not enough memory */
544 return STATUS_INSUFFICIENT_RESOURCES;
545 }
546
547 /* clear node index array */
548 RtlZeroMemory(Refs, Count * sizeof(ULONG));
549
550 Count = 0;
551 Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
552 for(Index = 0; Index < MultipleItem->Count; Index++)
553 {
554 if (bNode)
555 {
556 if (bFrom)
557 {
558 if (Connection->FromNode == NodeIndex)
559 {
560 /* node id has a connection */
561 Refs[Count] = Index;
562 Count++;
563 }
564 }
565 else
566 {
567 if (Connection->ToNode == NodeIndex)
568 {
569 /* node id has a connection */
570 Refs[Count] = Index;
571 Count++;
572 }
573 }
574 }
575 else
576 {
577 if (bFrom)
578 {
579 if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE)
580 {
581 /* node id has a connection */
582 Refs[Count] = Index;
583 Count++;
584 }
585 }
586 else
587 {
588 if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE)
589 {
590 /* node id has a connection */
591 Refs[Count] = Index;
592 Count++;
593 }
594 }
595 }
596
597 /* move to next connection */
598 Connection++;
599 }
600
601 /* store result */
602 *NodeReference = Refs;
603 *NodeReferenceCount = Count;
604
605 return STATUS_SUCCESS;
606 }
607
608
609 NTSTATUS
610 GetTargetPinsByNodeConnectionIndex(
611 IN PKSMULTIPLE_ITEM NodeConnections,
612 IN PKSMULTIPLE_ITEM NodeTypes,
613 IN ULONG bUpDirection,
614 IN ULONG NodeConnectionIndex,
615 OUT PULONG Pins)
616 {
617 PKSTOPOLOGY_CONNECTION Connection;
618 ULONG PinId, NodeConnectionCount, Index;
619 PULONG NodeConnection;
620 NTSTATUS Status;
621
622
623 /* sanity check */
624 ASSERT(NodeConnectionIndex < NodeConnections->Count);
625
626 Connection = (PKSTOPOLOGY_CONNECTION)(NodeConnections + 1);
627
628 DPRINT("FromNode %u FromNodePin %u -> ToNode %u ToNodePin %u\n", Connection[NodeConnectionIndex].FromNode, Connection[NodeConnectionIndex].FromNodePin, Connection[NodeConnectionIndex].ToNode, Connection[NodeConnectionIndex].ToNodePin );
629
630 if ((Connection[NodeConnectionIndex].ToNode == KSFILTER_NODE && bUpDirection == FALSE) ||
631 (Connection[NodeConnectionIndex].FromNode == KSFILTER_NODE && bUpDirection == TRUE))
632 {
633 /* iteration stops here */
634 if (bUpDirection)
635 PinId = Connection[NodeConnectionIndex].FromNodePin;
636 else
637 PinId = Connection[NodeConnectionIndex].ToNodePin;
638
639 DPRINT("GetTargetPinsByNodeIndex FOUND Target Pin %u Parsed %u\n", PinId, Pins[PinId]);
640
641 /* mark pin index as a target pin */
642 Pins[PinId] = TRUE;
643 return STATUS_SUCCESS;
644 }
645
646 /* get all node indexes referenced by that node */
647 if (bUpDirection)
648 {
649 Status = GetNodeIndexes(NodeConnections, Connection[NodeConnectionIndex].FromNode, TRUE, FALSE, &NodeConnectionCount, &NodeConnection);
650 }
651 else
652 {
653 Status = GetNodeIndexes(NodeConnections, Connection[NodeConnectionIndex].ToNode, TRUE, TRUE, &NodeConnectionCount, &NodeConnection);
654 }
655
656 if (NT_SUCCESS(Status))
657 {
658 for(Index = 0; Index < NodeConnectionCount; Index++)
659 {
660 /* iterate recursively into the nodes */
661 Status = GetTargetPinsByNodeConnectionIndex(NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], Pins);
662 ASSERT(Status == STATUS_SUCCESS);
663 }
664 /* free node connection indexes */
665 ExFreePool(NodeConnection);
666 }
667
668 return Status;
669 }
670
671
672
673 NTSTATUS
674 GetTargetPins(
675 PKSMULTIPLE_ITEM NodeTypes,
676 PKSMULTIPLE_ITEM NodeConnections,
677 IN ULONG NodeIndex,
678 IN ULONG bUpDirection,
679 PULONG Pins,
680 ULONG PinCount)
681 {
682 ULONG NodeConnectionCount, Index;
683 NTSTATUS Status;
684 PULONG NodeConnection;
685
686 /* sanity check */
687 ASSERT(NodeIndex != (ULONG)-1);
688
689 /* get all node indexes referenced by that pin */
690 if (bUpDirection)
691 Status = GetNodeIndexes(NodeConnections, NodeIndex, TRUE, FALSE, &NodeConnectionCount, &NodeConnection);
692 else
693 Status = GetNodeIndexes(NodeConnections, NodeIndex, TRUE, TRUE, &NodeConnectionCount, &NodeConnection);
694
695 DPRINT("NodeIndex %u Status %x Count %u\n", NodeIndex, Status, NodeConnectionCount);
696
697 if (NT_SUCCESS(Status))
698 {
699 for(Index = 0; Index < NodeConnectionCount; Index++)
700 {
701 Status = GetTargetPinsByNodeConnectionIndex(NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], Pins);
702 ASSERT(Status == STATUS_SUCCESS);
703 }
704 ExFreePool(NodeConnection);
705 }
706
707 return Status;
708 }
709
710 PULONG
711 AllocatePinArray(
712 ULONG PinCount)
713 {
714 PULONG Pins = ExAllocatePool(NonPagedPool, PinCount * sizeof(ULONG));
715 if (!Pins)
716 return NULL;
717
718 RtlZeroMemory(Pins, sizeof(ULONG) * PinCount);
719
720 return Pins;
721 }
722
723 PKSTOPOLOGY_CONNECTION
724 GetConnectionByIndex(
725 IN PKSMULTIPLE_ITEM MultipleItem,
726 IN ULONG Index)
727 {
728 PKSTOPOLOGY_CONNECTION Descriptor;
729
730 ASSERT(Index < MultipleItem->Count);
731
732 Descriptor = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
733 return &Descriptor[Index];
734 }
735
736 LPGUID
737 GetNodeType(
738 IN PKSMULTIPLE_ITEM MultipleItem,
739 IN ULONG Index)
740 {
741 LPGUID NodeType;
742
743 ASSERT(Index < MultipleItem->Count);
744
745 NodeType = (LPGUID)(MultipleItem + 1);
746 return &NodeType[Index];
747 }
748
749 NTSTATUS
750 GetControlsFromPinByConnectionIndex(
751 IN PKSMULTIPLE_ITEM NodeConnections,
752 IN PKSMULTIPLE_ITEM NodeTypes,
753 IN ULONG bUpDirection,
754 IN ULONG NodeConnectionIndex,
755 OUT PULONG Nodes)
756 {
757 PKSTOPOLOGY_CONNECTION CurConnection;
758 LPGUID NodeType;
759 ULONG NodeIndex;
760 NTSTATUS Status;
761 ULONG NodeConnectionCount, Index;
762 PULONG NodeConnection;
763
764
765 /* get current connection */
766 CurConnection = GetConnectionByIndex(NodeConnections, NodeConnectionIndex);
767
768 if (bUpDirection)
769 NodeIndex = CurConnection->FromNode;
770 else
771 NodeIndex = CurConnection->ToNode;
772
773 /* get target node type of current connection */
774 NodeType = GetNodeType(NodeTypes, NodeIndex);
775
776 if (IsEqualGUIDAligned(NodeType, &KSNODETYPE_SUM) || IsEqualGUIDAligned(NodeType, &KSNODETYPE_MUX))
777 {
778 if (bUpDirection)
779 {
780 /* add the sum / mux node to destination line */
781 //Nodes[NodeIndex] = TRUE;
782 }
783
784 return STATUS_SUCCESS;
785 }
786
787 /* now add the node */
788 Nodes[NodeIndex] = TRUE;
789
790
791 /* get all node indexes referenced by that node */
792 if (bUpDirection)
793 {
794 Status = GetNodeIndexes(NodeConnections, NodeIndex, TRUE, FALSE, &NodeConnectionCount, &NodeConnection);
795 }
796 else
797 {
798 Status = GetNodeIndexes(NodeConnections, NodeIndex, TRUE, TRUE, &NodeConnectionCount, &NodeConnection);
799 }
800
801 if (NT_SUCCESS(Status))
802 {
803 for(Index = 0; Index < NodeConnectionCount; Index++)
804 {
805 /* iterate recursively into the nodes */
806 Status = GetControlsFromPinByConnectionIndex(NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], Nodes);
807 ASSERT(Status == STATUS_SUCCESS);
808 }
809 /* free node connection indexes */
810 ExFreePool(NodeConnection);
811 }
812
813 return Status;
814 }
815
816 NTSTATUS
817 GetControlsFromPin(
818 IN PKSMULTIPLE_ITEM NodeConnections,
819 IN PKSMULTIPLE_ITEM NodeTypes,
820 IN ULONG PinId,
821 IN ULONG bUpDirection,
822 OUT PULONG Nodes)
823 {
824 ULONG NodeConnectionCount, Index;
825 NTSTATUS Status;
826 PULONG NodeConnection;
827
828 /* sanity check */
829 ASSERT(PinId != (ULONG)-1);
830
831 /* get all node indexes referenced by that pin */
832 if (bUpDirection)
833 Status = GetNodeIndexes(NodeConnections, PinId, FALSE, FALSE, &NodeConnectionCount, &NodeConnection);
834 else
835 Status = GetNodeIndexes(NodeConnections, PinId, FALSE, TRUE, &NodeConnectionCount, &NodeConnection);
836
837 for(Index = 0; Index < NodeConnectionCount; Index++)
838 {
839 /* get all associated controls */
840 Status = GetControlsFromPinByConnectionIndex(NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], Nodes);
841 }
842
843 ExFreePool(NodeConnection);
844
845 return Status;
846 }
847
848 NTSTATUS
849 AddMixerControl(
850 IN LPMIXER_INFO MixerInfo,
851 IN PFILE_OBJECT FileObject,
852 IN PKSMULTIPLE_ITEM NodeTypes,
853 IN ULONG NodeIndex,
854 OUT LPMIXERCONTROLW MixerControl)
855 {
856 LPGUID NodeType;
857 KSP_NODE Node;
858 ULONG BytesReturned;
859 NTSTATUS Status;
860 LPWSTR Name;
861
862
863 /* initialize mixer control */
864 MixerControl->cbStruct = sizeof(MIXERCONTROLW);
865 MixerControl->dwControlID = MixerInfo->ControlId;
866
867 /* get node type */
868 NodeType = GetNodeType(NodeTypes, NodeIndex);
869 /* store control type */
870 MixerControl->dwControlType = GetControlTypeFromTopologyNode(NodeType);
871
872 MixerControl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM; //FIXME
873 MixerControl->cMultipleItems = 0; //FIXME
874
875 if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
876 {
877 MixerControl->Bounds.dwMinimum = 0;
878 MixerControl->Bounds.dwMaximum = 1;
879 }
880 else if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
881 {
882 MixerControl->Bounds.dwMinimum = 0;
883 MixerControl->Bounds.dwMaximum = 0xFFFF;
884 MixerControl->Metrics.cSteps = 0xC0; //FIXME
885 }
886
887 /* setup request to retrieve name */
888 Node.NodeId = NodeIndex;
889 Node.Property.Id = KSPROPERTY_TOPOLOGY_NAME;
890 Node.Property.Flags = KSPROPERTY_TYPE_GET;
891 Node.Property.Set = KSPROPSETID_Topology;
892 Node.Reserved = 0;
893
894 /* get node name size */
895 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), NULL, 0, &BytesReturned);
896
897 if (Status == STATUS_BUFFER_TOO_SMALL)
898 {
899 ASSERT(BytesReturned != 0);
900 Name = ExAllocatePool(NonPagedPool, BytesReturned);
901 if (!Name)
902 {
903 /* not enough memory */
904 return STATUS_INSUFFICIENT_RESOURCES;
905 }
906
907 /* get node name */
908 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), (LPVOID)Name, BytesReturned, &BytesReturned);
909 if (NT_SUCCESS(Status))
910 {
911 RtlMoveMemory(MixerControl->szShortName, Name, (min(MIXER_SHORT_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
912 MixerControl->szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
913
914 RtlMoveMemory(MixerControl->szName, Name, (min(MIXER_LONG_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
915 MixerControl->szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
916 }
917
918 /* free name buffer */
919 ExFreePool(Name);
920 }
921
922 MixerInfo->ControlId++;
923
924 DPRINT("Status %x Name %S\n", Status, MixerControl->szName);
925 return STATUS_SUCCESS;
926 }
927
928 NTSTATUS
929 AddMixerSourceLine(
930 IN OUT LPMIXER_INFO MixerInfo,
931 IN PFILE_OBJECT FileObject,
932 IN PKSMULTIPLE_ITEM NodeConnections,
933 IN PKSMULTIPLE_ITEM NodeTypes,
934 IN ULONG DeviceIndex,
935 IN ULONG PinId,
936 IN ULONG bBridgePin,
937 IN ULONG bTargetPin)
938 {
939 LPMIXERLINE_EXT SrcLine, DstLine;
940 NTSTATUS Status;
941 KSP_PIN Pin;
942 LPWSTR PinName;
943 GUID NodeType;
944 ULONG BytesReturned, ControlCount, Index;
945 PULONG Nodes;
946
947 if (!bTargetPin)
948 {
949 /* allocate src mixer line */
950 SrcLine = (LPMIXERLINE_EXT)ExAllocatePool(NonPagedPool, sizeof(MIXERLINE_EXT));
951
952 if (!SrcLine)
953 return STATUS_INSUFFICIENT_RESOURCES;
954
955 /* zero struct */
956 RtlZeroMemory(SrcLine, sizeof(MIXERLINE_EXT));
957
958 }
959 else
960 {
961 ASSERT(!IsListEmpty(&MixerInfo->LineList));
962 SrcLine = GetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
963 }
964
965 /* get destination line */
966 DstLine = GetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
967 ASSERT(DstLine);
968
969
970 if (!bTargetPin)
971 {
972 /* initialize mixer src line */
973 SrcLine->DeviceIndex = DeviceIndex;
974 SrcLine->PinId = PinId;
975 SrcLine->Line.cbStruct = sizeof(MIXERLINEW);
976
977 /* initialize mixer destination line */
978 SrcLine->Line.cbStruct = sizeof(MIXERLINEW);
979 SrcLine->Line.dwDestination = 0;
980 SrcLine->Line.dwSource = DstLine->Line.cConnections;
981 SrcLine->Line.dwLineID = (DstLine->Line.cConnections * 0x10000);
982 SrcLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
983 SrcLine->Line.dwUser = 0;
984 SrcLine->Line.cChannels = DstLine->Line.cChannels;
985 SrcLine->Line.cConnections = 0;
986 SrcLine->Line.Target.dwType = 1;
987 SrcLine->Line.Target.dwDeviceID = DstLine->Line.Target.dwDeviceID;
988 SrcLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
989 SrcLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
990 SrcLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
991 wcscpy(SrcLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
992
993 }
994
995 /* allocate a node arrary */
996 Nodes = ExAllocatePool(NonPagedPool, sizeof(ULONG) * NodeTypes->Count);
997
998 if (!Nodes)
999 {
1000 /* not enough memory */
1001 if (!bTargetPin)
1002 {
1003 ExFreePool(SrcLine);
1004 }
1005 return STATUS_INSUFFICIENT_RESOURCES;
1006 }
1007
1008 /* clear nodes array */
1009 RtlZeroMemory(Nodes, sizeof(ULONG) * NodeTypes->Count);
1010
1011 Status = GetControlsFromPin(NodeConnections, NodeTypes, PinId, bTargetPin, Nodes);
1012 if (!NT_SUCCESS(Status))
1013 {
1014 /* something went wrong */
1015 if (!bTargetPin)
1016 {
1017 ExFreePool(SrcLine);
1018 }
1019 ExFreePool(Nodes);
1020 return Status;
1021 }
1022
1023 /* now count all nodes controlled by that pin */
1024 ControlCount = 0;
1025 for(Index = 0; Index < NodeTypes->Count; Index++)
1026 {
1027 if (Nodes[Index])
1028 ControlCount++;
1029 }
1030
1031 /* now allocate the line controls */
1032 if (ControlCount)
1033 {
1034 SrcLine->LineControls = ExAllocatePool(NonPagedPool, sizeof(MIXERCONTROLW) * ControlCount);
1035
1036 if (!SrcLine->LineControls)
1037 {
1038 /* no memory available */
1039 if (!bTargetPin)
1040 {
1041 ExFreePool(SrcLine);
1042 }
1043 ExFreePool(Nodes);
1044 return STATUS_INSUFFICIENT_RESOURCES;
1045 }
1046
1047 SrcLine->NodeIds = ExAllocatePool(NonPagedPool, sizeof(ULONG) * ControlCount);
1048 if (!SrcLine->NodeIds)
1049 {
1050 /* no memory available */
1051 ExFreePool(SrcLine->LineControls);
1052 if (!bTargetPin)
1053 {
1054 ExFreePool(SrcLine);
1055 }
1056 ExFreePool(Nodes);
1057 return STATUS_INSUFFICIENT_RESOURCES;
1058 }
1059
1060 /* zero line controls */
1061 RtlZeroMemory(SrcLine->LineControls, sizeof(MIXERCONTROLW) * ControlCount);
1062 RtlZeroMemory(SrcLine->NodeIds, sizeof(ULONG) * ControlCount);
1063
1064 ControlCount = 0;
1065 for(Index = 0; Index < NodeTypes->Count; Index++)
1066 {
1067 if (Nodes[Index])
1068 {
1069 /* store the node index for retrieving / setting details */
1070 SrcLine->NodeIds[ControlCount] = Index;
1071
1072 Status = AddMixerControl(MixerInfo, FileObject, NodeTypes, Index, &SrcLine->LineControls[ControlCount]);
1073 if (NT_SUCCESS(Status))
1074 {
1075 /* increment control count on success */
1076 ControlCount++;
1077 }
1078 }
1079 }
1080 /* store control count */
1081 SrcLine->Line.cControls = ControlCount;
1082 }
1083
1084 /* release nodes array */
1085 ExFreePool(Nodes);
1086
1087 /* get pin category */
1088 Pin.PinId = PinId;
1089 Pin.Reserved = 0;
1090 Pin.Property.Flags = KSPROPERTY_TYPE_GET;
1091 Pin.Property.Set = KSPROPSETID_Pin;
1092 Pin.Property.Id = KSPROPERTY_PIN_CATEGORY;
1093
1094 /* try get pin category */
1095 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (LPVOID)&NodeType, sizeof(GUID), &BytesReturned);
1096 if (NT_SUCCESS(Status))
1097 {
1098 //FIXME
1099 //map component type
1100 }
1101
1102 /* retrieve pin name */
1103 Pin.PinId = PinId;
1104 Pin.Reserved = 0;
1105 Pin.Property.Flags = KSPROPERTY_TYPE_GET;
1106 Pin.Property.Set = KSPROPSETID_Pin;
1107 Pin.Property.Id = KSPROPERTY_PIN_NAME;
1108
1109 /* try get pin name size */
1110 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
1111
1112 if (Status != STATUS_MORE_ENTRIES)
1113 {
1114 SrcLine->Line.szShortName[0] = L'\0';
1115 SrcLine->Line.szName[0] = L'\0';
1116 }
1117 else
1118 {
1119 PinName = (LPWSTR)ExAllocatePool(NonPagedPool, BytesReturned);
1120 if (PinName)
1121 {
1122 /* try get pin name */
1123 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (LPVOID)PinName, BytesReturned, &BytesReturned);
1124
1125 if (NT_SUCCESS(Status))
1126 {
1127 RtlMoveMemory(SrcLine->Line.szShortName, PinName, (min(MIXER_SHORT_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
1128 SrcLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
1129
1130 RtlMoveMemory(SrcLine->Line.szName, PinName, (min(MIXER_LONG_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
1131 SrcLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
1132 }
1133 ExFreePool(PinName);
1134 }
1135 }
1136
1137 /* insert src line */
1138 if (!bTargetPin)
1139 {
1140 InsertTailList(&MixerInfo->LineList, &SrcLine->Entry);
1141 DstLine->Line.cConnections++;
1142 }
1143
1144 return STATUS_SUCCESS;
1145 }
1146
1147
1148 NTSTATUS
1149 AddMixerSourceLines(
1150 IN OUT LPMIXER_INFO MixerInfo,
1151 IN PFILE_OBJECT FileObject,
1152 IN PKSMULTIPLE_ITEM NodeConnections,
1153 IN PKSMULTIPLE_ITEM NodeTypes,
1154 IN ULONG DeviceIndex,
1155 IN ULONG PinsCount,
1156 IN ULONG BridgePinIndex,
1157 IN ULONG TargetPinIndex,
1158 IN PULONG Pins)
1159 {
1160 ULONG Index;
1161 NTSTATUS Status = STATUS_SUCCESS;
1162
1163 for(Index = PinsCount; Index > 0; Index--)
1164 {
1165 if (Pins[Index-1])
1166 {
1167 AddMixerSourceLine(MixerInfo, FileObject, NodeConnections, NodeTypes, DeviceIndex, Index-1, (Index -1 == BridgePinIndex), (Index -1 == TargetPinIndex));
1168 }
1169 }
1170 return Status;
1171 }
1172
1173
1174
1175 NTSTATUS
1176 HandlePhysicalConnection(
1177 IN OUT LPMIXER_INFO MixerInfo,
1178 IN PDEVICE_OBJECT DeviceObject,
1179 IN ULONG bInput,
1180 IN PKSPIN_PHYSICALCONNECTION OutConnection)
1181 {
1182 PULONG PinsRef = NULL, PinConnectionIndex = NULL, PinsSrcRef;
1183 ULONG PinsRefCount, Index, PinConnectionIndexCount, DeviceIndex;
1184 NTSTATUS Status;
1185 HANDLE hDevice = NULL;
1186 PFILE_OBJECT FileObject = NULL;
1187 PKSMULTIPLE_ITEM NodeTypes = NULL;
1188 PKSMULTIPLE_ITEM NodeConnections = NULL;
1189 PULONG MixerControls;
1190 ULONG MixerControlsCount;
1191
1192
1193 /* open the connected filter */
1194 Status = OpenDevice(OutConnection->SymbolicLinkName, &hDevice, &FileObject);
1195 if (!NT_SUCCESS(Status))
1196 {
1197 DPRINT1("OpenDevice failed with %x\n", Status);
1198 return Status;
1199 }
1200
1201 /* get device index */
1202 DeviceIndex = GetDeviceIndexFromPnpName(DeviceObject, OutConnection->SymbolicLinkName);
1203
1204 /* get connected filter pin count */
1205 PinsRefCount = GetPinCount(FileObject);
1206 ASSERT(PinsRefCount);
1207
1208 PinsRef = AllocatePinArray(PinsRefCount);
1209 if (!PinsRef)
1210 {
1211 /* no memory */
1212 Status = STATUS_INSUFFICIENT_RESOURCES;
1213 goto cleanup;
1214 }
1215
1216 /* get topology node types */
1217 Status = GetFilterNodeProperty(FileObject, KSPROPERTY_TOPOLOGY_NODES, &NodeTypes);
1218 if (!NT_SUCCESS(Status))
1219 {
1220 DPRINT1("GetFilterNodeProperty failed with %x\n", Status);
1221 goto cleanup;
1222 }
1223
1224 /* get topology connections */
1225 Status = GetFilterNodeProperty(FileObject, KSPROPERTY_TOPOLOGY_CONNECTIONS, &NodeConnections);
1226 if (!NT_SUCCESS(Status))
1227 {
1228 DPRINT1("GetFilterNodeProperty failed with %x\n", Status);
1229 goto cleanup;
1230 }
1231 /* gets connection index of the bridge pin which connects to a node */
1232 DPRINT("Pin %u\n", OutConnection->Pin);
1233 Status = GetNodeIndexes(NodeConnections, OutConnection->Pin, FALSE, !bInput, &PinConnectionIndexCount, &PinConnectionIndex);
1234 if (!NT_SUCCESS(Status))
1235 {
1236 DPRINT1("GetNodeIndexes failed with %x\n", Status);
1237 goto cleanup;
1238 }
1239
1240 /* there should be no split in the bride pin */
1241 ASSERT(PinConnectionIndexCount == 1);
1242
1243 /* find all target pins of this connection */
1244 Status = GetTargetPinsByNodeConnectionIndex(NodeConnections, NodeTypes, FALSE, PinConnectionIndex[0], PinsRef);
1245 if (!NT_SUCCESS(Status))
1246 {
1247 DPRINT1("GetTargetPinsByNodeConnectionIndex failed with %x\n", Status);
1248 goto cleanup;
1249 }
1250
1251 for(Index = 0; Index < PinsRefCount; Index++)
1252 {
1253 if (PinsRef[Index])
1254 {
1255
1256 /* found a target pin, now get all references */
1257 Status = GetNodeIndexes(NodeConnections, Index, FALSE, FALSE, &MixerControlsCount, &MixerControls);
1258 if (!NT_SUCCESS(Status))
1259 break;
1260
1261 /* sanity check */
1262 ASSERT(MixerControlsCount == 1);
1263
1264
1265 PinsSrcRef = AllocatePinArray(PinsRefCount);
1266 if (!PinsSrcRef)
1267 {
1268 /* no memory */
1269 ExFreePool(MixerControls);
1270 Status = STATUS_INSUFFICIENT_RESOURCES;
1271 goto cleanup;
1272 }
1273 /* now get all connected source pins */
1274 Status = GetTargetPinsByNodeConnectionIndex(NodeConnections, NodeTypes, TRUE, MixerControls[0], PinsSrcRef);
1275 if (!NT_SUCCESS(Status))
1276 {
1277 /* no memory */
1278 ExFreePool(MixerControls);
1279 ExFreePool(PinsSrcRef);
1280 Status = STATUS_INSUFFICIENT_RESOURCES;
1281 goto cleanup;
1282 }
1283
1284 /* add pins from target line */
1285 if (!bInput)
1286 {
1287 // dont add bridge pin for input mixers
1288 PinsSrcRef[Index] = TRUE;
1289 PinsSrcRef[OutConnection->Pin] = TRUE;
1290 }
1291
1292 Status = AddMixerSourceLines(MixerInfo, FileObject, NodeConnections, NodeTypes, DeviceIndex, PinsRefCount, OutConnection->Pin, Index, PinsSrcRef);
1293
1294 ExFreePool(MixerControls);
1295 ExFreePool(PinsSrcRef);
1296 }
1297 }
1298
1299 cleanup:
1300
1301 if (PinsRef)
1302 ExFreePool(PinsRef);
1303
1304 if (NodeConnections)
1305 ExFreePool(NodeConnections);
1306
1307 if (NodeTypes)
1308 ExFreePool(NodeTypes);
1309
1310 if (FileObject)
1311 ObDereferenceObject(FileObject);
1312
1313 if (hDevice)
1314 ZwClose(hDevice);
1315
1316 if (PinConnectionIndex)
1317 ExFreePool(PinConnectionIndex);
1318
1319
1320 return Status;
1321 }
1322
1323
1324
1325 NTSTATUS
1326 InitializeMixer(
1327 IN PDEVICE_OBJECT DeviceObject,
1328 IN ULONG DeviceIndex,
1329 IN OUT LPMIXER_INFO MixerInfo,
1330 IN HANDLE hDevice,
1331 IN PFILE_OBJECT FileObject,
1332 IN ULONG PinCount,
1333 IN PKSMULTIPLE_ITEM NodeTypes,
1334 IN PKSMULTIPLE_ITEM NodeConnections,
1335 IN ULONG NodeIndex,
1336 IN ULONG bInput)
1337 {
1338 WCHAR Buffer[100];
1339 LPWSTR Device;
1340 NTSTATUS Status;
1341 PULONG Pins;
1342 ULONG Index;
1343 PKSPIN_PHYSICALCONNECTION OutConnection;
1344 LPMIXERLINE_EXT DestinationLine;
1345
1346 DestinationLine = ExAllocatePool(NonPagedPool, sizeof(MIXERLINE_EXT));
1347 if (!DestinationLine)
1348 return STATUS_INSUFFICIENT_RESOURCES;
1349
1350 /* intialize mixer caps */
1351 MixerInfo->MixCaps.wMid = MM_MICROSOFT; //FIXME
1352 MixerInfo->MixCaps.wPid = MM_PID_UNMAPPED; //FIXME
1353 MixerInfo->MixCaps.vDriverVersion = 1; //FIXME
1354 MixerInfo->MixCaps.fdwSupport = 0;
1355 MixerInfo->MixCaps.cDestinations = 1;
1356
1357 /* get target pnp name */
1358 Status = GetSysAudioDevicePnpName(DeviceObject, DeviceIndex, &Device);
1359 if (NT_SUCCESS(Status))
1360 {
1361 /* find product name */
1362 Status = FindProductName(Device, sizeof(Buffer) / sizeof(WCHAR), Buffer);
1363 if (NT_SUCCESS(Status))
1364 {
1365 if (bInput)
1366 wcscat(Buffer, L" Input");
1367 else
1368 wcscat(Buffer, L" output");
1369 RtlMoveMemory(MixerInfo->MixCaps.szPname, Buffer, min(MAXPNAMELEN, wcslen(Buffer)+1) * sizeof(WCHAR));
1370 MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] = L'\0';
1371 }
1372 ExFreePool(Device);
1373 }
1374
1375 /* initialize mixer destination line */
1376 RtlZeroMemory(DestinationLine, sizeof(MIXERLINEW));
1377 DestinationLine->Line.cbStruct = sizeof(MIXERLINEW);
1378 DestinationLine->Line.dwSource = MAXULONG;
1379 DestinationLine->Line.dwLineID = DESTINATION_LINE;
1380 DestinationLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE;
1381 DestinationLine->Line.dwUser = 0;
1382 DestinationLine->Line.dwComponentType = (bInput == 0 ? MIXERLINE_COMPONENTTYPE_DST_SPEAKERS : MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
1383 DestinationLine->Line.cChannels = 2; //FIXME
1384 wcscpy(DestinationLine->Line.szShortName, L"Summe"); //FIXME
1385 wcscpy(DestinationLine->Line.szName, L"Summe"); //FIXME
1386 DestinationLine->Line.Target.dwType = (bInput == 0 ? MIXERLINE_TARGETTYPE_WAVEOUT : MIXERLINE_TARGETTYPE_WAVEIN);
1387 DestinationLine->Line.Target.dwDeviceID = !bInput;
1388 DestinationLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
1389 DestinationLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
1390 DestinationLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
1391 wcscpy(DestinationLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
1392
1393 /* initialize source line list */
1394 InitializeListHead(&MixerInfo->LineList);
1395
1396 /* insert destination line */
1397 InsertHeadList(&MixerInfo->LineList, &DestinationLine->Entry);
1398
1399 Pins = AllocatePinArray(PinCount);
1400 if (!Pins)
1401 return STATUS_INSUFFICIENT_RESOURCES;
1402
1403 if (bInput)
1404 {
1405 Status = GetTargetPins(NodeTypes, NodeConnections, NodeIndex, TRUE, Pins, PinCount);
1406 }
1407 else
1408 {
1409 Status = GetTargetPins(NodeTypes, NodeConnections, NodeIndex, FALSE, Pins, PinCount);
1410 }
1411
1412 for(Index = 0; Index < PinCount; Index++)
1413 {
1414 if (Pins[Index])
1415 {
1416 Status = GetPhysicalConnection(FileObject, Index, &OutConnection);
1417 if (NT_SUCCESS(Status))
1418 {
1419 Status = HandlePhysicalConnection(MixerInfo, DeviceObject, bInput, OutConnection);
1420 ExFreePool(OutConnection);
1421 }
1422 }
1423 }
1424 ExFreePool(Pins);
1425
1426 return STATUS_SUCCESS;
1427 }
1428
1429 NTSTATUS
1430 WdmAudMixerInitialize(
1431 IN PDEVICE_OBJECT DeviceObject)
1432 {
1433 ULONG DeviceCount, Index, Count, NodeIndex, PinCount;
1434 NTSTATUS Status;
1435 HANDLE hDevice;
1436 PFILE_OBJECT FileObject;
1437 PKSMULTIPLE_ITEM NodeTypes, NodeConnections;
1438 PWDMAUD_DEVICE_EXTENSION DeviceExtension;
1439
1440 /* get device extension */
1441 DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1442
1443
1444 /* get number of devices */
1445 DeviceCount = GetSysAudioDeviceCount(DeviceObject);
1446
1447 if (!DeviceCount)
1448 {
1449 /* no audio devices available atm */
1450 DeviceExtension->MixerInfoCount = 0;
1451 DeviceExtension->MixerInfo = NULL;
1452 return STATUS_SUCCESS;
1453 }
1454
1455 /* each virtual audio device can at most have an input + output mixer */
1456 DeviceExtension->MixerInfo = ExAllocatePool(NonPagedPool, sizeof(MIXER_INFO) * DeviceCount * 2);
1457 if (!DeviceExtension->MixerInfo)
1458 {
1459 /* not enough memory */
1460 return STATUS_INSUFFICIENT_RESOURCES;
1461 }
1462
1463 /* clear mixer info */
1464 RtlZeroMemory(DeviceExtension->MixerInfo, sizeof(MIXER_INFO) * DeviceCount * 2);
1465
1466 Index = 0;
1467 Count = 0;
1468 do
1469 {
1470 /* open the virtual audio device */
1471 Status = OpenSysAudioDeviceByIndex(DeviceObject, Index, &hDevice, &FileObject);
1472
1473 if (NT_SUCCESS(Status))
1474 {
1475 /* retrieve all available node types */
1476 Status = GetFilterNodeProperty(FileObject, KSPROPERTY_TOPOLOGY_NODES, &NodeTypes);
1477 if (!NT_SUCCESS(Status))
1478 {
1479 ObDereferenceObject(FileObject);
1480 ZwClose(hDevice);
1481 break;
1482 }
1483
1484 Status = GetFilterNodeProperty(FileObject, KSPROPERTY_TOPOLOGY_CONNECTIONS, &NodeConnections);
1485 if (!NT_SUCCESS(Status))
1486 {
1487 ObDereferenceObject(FileObject);
1488 ZwClose(hDevice);
1489 ExFreePool(NodeTypes);
1490 break;
1491 }
1492
1493 /* get num of pins */
1494 PinCount = GetPinCount(FileObject);
1495 /* get the first available dac node index */
1496 NodeIndex = GetNodeTypeIndex(NodeTypes, (LPGUID)&KSNODETYPE_DAC);
1497 if (NodeIndex != (ULONG)-1)
1498 {
1499 Status = InitializeMixer(DeviceObject, Index, &DeviceExtension->MixerInfo[Count], hDevice, FileObject, PinCount, NodeTypes, NodeConnections, NodeIndex, FALSE);
1500 if (NT_SUCCESS(Status))
1501 {
1502 /* increment mixer offset */
1503 Count++;
1504 }
1505 }
1506
1507 /* get the first available adc node index */
1508 NodeIndex = GetNodeTypeIndex(NodeTypes, (LPGUID)&KSNODETYPE_ADC);
1509 if (NodeIndex != (ULONG)-1)
1510 {
1511 Status = InitializeMixer(DeviceObject, Index, &DeviceExtension->MixerInfo[Count], hDevice, FileObject, PinCount, NodeTypes, NodeConnections, NodeIndex, TRUE);
1512 if (NT_SUCCESS(Status))
1513 {
1514 /* increment mixer offset */
1515 Count++;
1516 }
1517 }
1518
1519 /* free node connections array */
1520 ExFreePool(NodeTypes);
1521 ExFreePool(NodeConnections);
1522
1523 /* close virtual audio device */
1524 ObDereferenceObject(FileObject);
1525 ZwClose(hDevice);
1526
1527 }
1528 /* increment virtual audio device index */
1529 Index++;
1530 }while(Index < DeviceCount);
1531
1532 /* store mixer count */
1533 DeviceExtension->MixerInfoCount = Count;
1534
1535 return Status;
1536 }
1537
1538
1539
1540 NTSTATUS
1541 WdmAudMixerCapabilities(
1542 IN PDEVICE_OBJECT DeviceObject,
1543 IN PWDMAUD_DEVICE_INFO DeviceInfo,
1544 IN PWDMAUD_CLIENT ClientInfo,
1545 IN PWDMAUD_DEVICE_EXTENSION DeviceExtension)
1546 {
1547 if ((ULONG)DeviceInfo->hDevice >= DeviceExtension->MixerInfoCount)
1548 {
1549 /* invalid parameter */
1550 return STATUS_INVALID_PARAMETER;
1551 }
1552
1553 /* copy cached mixer caps */
1554 RtlMoveMemory(&DeviceInfo->u.MixCaps, &DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice].MixCaps, sizeof(MIXERCAPSW));
1555
1556 return STATUS_SUCCESS;
1557 }
1558
1559
1560 NTSTATUS
1561 WdmAudControlOpenMixer(
1562 IN PDEVICE_OBJECT DeviceObject,
1563 IN PIRP Irp,
1564 IN PWDMAUD_DEVICE_INFO DeviceInfo,
1565 IN PWDMAUD_CLIENT ClientInfo)
1566 {
1567 ULONG Index;
1568 PWDMAUD_HANDLE Handels;
1569 PWDMAUD_DEVICE_EXTENSION DeviceExtension;
1570
1571 DPRINT("WdmAudControlOpenMixer\n");
1572
1573 DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1574
1575
1576 if (DeviceInfo->DeviceIndex >= DeviceExtension->MixerInfoCount)
1577 {
1578 /* mixer index doesnt exist */
1579 return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, 0);
1580 }
1581
1582 for(Index = 0; Index < ClientInfo->NumPins; Index++)
1583 {
1584 if (ClientInfo->hPins[Index].Handle == (HANDLE)DeviceInfo->DeviceIndex && ClientInfo->hPins[Index].Type == MIXER_DEVICE_TYPE)
1585 {
1586 /* re-use pseudo handle */
1587 DeviceInfo->hDevice = (HANDLE)DeviceInfo->DeviceIndex;
1588 return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
1589 }
1590 }
1591
1592 Handels = ExAllocatePool(NonPagedPool, sizeof(WDMAUD_HANDLE) * (ClientInfo->NumPins+1));
1593
1594 if (Handels)
1595 {
1596 if (ClientInfo->NumPins)
1597 {
1598 RtlMoveMemory(Handels, ClientInfo->hPins, sizeof(WDMAUD_HANDLE) * ClientInfo->NumPins);
1599 ExFreePool(ClientInfo->hPins);
1600 }
1601
1602 ClientInfo->hPins = Handels;
1603 ClientInfo->hPins[ClientInfo->NumPins].Handle = (HANDLE)DeviceInfo->DeviceIndex;
1604 ClientInfo->hPins[ClientInfo->NumPins].Type = MIXER_DEVICE_TYPE;
1605 ClientInfo->NumPins++;
1606 }
1607 else
1608 {
1609 return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, sizeof(WDMAUD_DEVICE_INFO));
1610 }
1611 DeviceInfo->hDevice = (HANDLE)DeviceInfo->DeviceIndex;
1612
1613 return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
1614 }
1615
1616 NTSTATUS
1617 NTAPI
1618 WdmAudGetLineInfo(
1619 IN PDEVICE_OBJECT DeviceObject,
1620 IN PIRP Irp,
1621 IN PWDMAUD_DEVICE_INFO DeviceInfo,
1622 IN PWDMAUD_CLIENT ClientInfo)
1623 {
1624 PWDMAUD_DEVICE_EXTENSION DeviceExtension;
1625 LPMIXERLINE_EXT MixerLineSrc;
1626
1627 /* get device extension */
1628 DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1629
1630 if (DeviceInfo->Flags == MIXER_GETLINEINFOF_DESTINATION)
1631 {
1632 if ((ULONG)DeviceInfo->hDevice >= DeviceExtension->MixerInfoCount)
1633 {
1634 /* invalid parameter */
1635 return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
1636 }
1637
1638 if (DeviceInfo->u.MixLine.dwDestination != 0)
1639 {
1640 /* invalid parameter */
1641 return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
1642 }
1643 MixerLineSrc = GetSourceMixerLineByLineId(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DESTINATION_LINE);
1644 ASSERT(MixerLineSrc);
1645
1646 /* copy cached data */
1647 RtlCopyMemory(&DeviceInfo->u.MixLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
1648 return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
1649 }
1650 else if (DeviceInfo->Flags == MIXER_GETLINEINFOF_SOURCE)
1651 {
1652 if ((ULONG)DeviceInfo->hDevice >= DeviceExtension->MixerInfoCount)
1653 {
1654 /* invalid parameter */
1655 return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
1656 }
1657
1658 MixerLineSrc = GetSourceMixerLineByLineId(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DESTINATION_LINE);
1659 ASSERT(MixerLineSrc);
1660
1661 if (DeviceInfo->u.MixLine.dwSource >= MixerLineSrc->Line.cConnections)
1662 {
1663 DPRINT1("dwSource %u Destinations %u\n", DeviceInfo->u.MixLine.dwSource, MixerLineSrc->Line.cConnections);
1664 /* invalid parameter */
1665 return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
1666 }
1667
1668 MixerLineSrc = GetSourceMixerLine(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DeviceInfo->u.MixLine.dwSource);
1669 if (MixerLineSrc)
1670 {
1671 DPRINT("Line %u Name %S\n", MixerLineSrc->Line.dwSource, MixerLineSrc->Line.szName);
1672 RtlCopyMemory(&DeviceInfo->u.MixLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
1673 }
1674 return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
1675 }
1676 else if (DeviceInfo->Flags == MIXER_GETLINEINFOF_LINEID)
1677 {
1678 if ((ULONG)DeviceInfo->hDevice >= DeviceExtension->MixerInfoCount)
1679 {
1680 /* invalid parameter */
1681 return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
1682 }
1683
1684 MixerLineSrc = GetSourceMixerLineByLineId(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DeviceInfo->u.MixLine.dwLineID);
1685 ASSERT(MixerLineSrc);
1686
1687 /* copy cached data */
1688 RtlCopyMemory(&DeviceInfo->u.MixLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
1689 return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
1690 }
1691
1692
1693 DPRINT1("Flags %x\n", DeviceInfo->Flags);
1694 UNIMPLEMENTED;
1695
1696 //DbgBreakPoint();
1697 return SetIrpIoStatus(Irp, STATUS_NOT_IMPLEMENTED, 0);
1698
1699 }
1700
1701 NTSTATUS
1702 NTAPI
1703 WdmAudGetLineControls(
1704 IN PDEVICE_OBJECT DeviceObject,
1705 IN PIRP Irp,
1706 IN PWDMAUD_DEVICE_INFO DeviceInfo,
1707 IN PWDMAUD_CLIENT ClientInfo)
1708 {
1709 LPMIXERLINE_EXT MixerLineSrc;
1710 PWDMAUD_DEVICE_EXTENSION DeviceExtension;
1711 ULONG Index;
1712
1713 /* get device extension */
1714 DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
1715
1716 if (DeviceInfo->Flags == MIXER_GETLINECONTROLSF_ALL)
1717 {
1718 if ((ULONG)DeviceInfo->hDevice >= DeviceExtension->MixerInfoCount)
1719 {
1720 /* invalid parameter */
1721 return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
1722 }
1723
1724 MixerLineSrc = GetSourceMixerLineByLineId(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DeviceInfo->u.MixControls.dwLineID);
1725 ASSERT(MixerLineSrc);
1726 if (MixerLineSrc)
1727 {
1728 RtlMoveMemory(DeviceInfo->u.MixControls.pamxctrl, MixerLineSrc->LineControls, min(MixerLineSrc->Line.cControls, DeviceInfo->u.MixControls.cControls) * sizeof(MIXERCONTROLW));
1729 }
1730 return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
1731 }
1732 else if (DeviceInfo->Flags == MIXER_GETLINECONTROLSF_ONEBYTYPE)
1733 {
1734 if ((ULONG)DeviceInfo->hDevice >= DeviceExtension->MixerInfoCount)
1735 {
1736 /* invalid parameter */
1737 return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
1738 }
1739
1740 MixerLineSrc = GetSourceMixerLineByLineId(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DeviceInfo->u.MixControls.dwLineID);
1741 ASSERT(MixerLineSrc);
1742
1743 Index = 0;
1744 for(Index = 0; Index < MixerLineSrc->Line.cControls; Index++)
1745 {
1746 if (DeviceInfo->u.MixControls.dwControlType == MixerLineSrc->LineControls[Index].dwControlType)
1747 {
1748 RtlMoveMemory(DeviceInfo->u.MixControls.pamxctrl, &MixerLineSrc->LineControls[Index], sizeof(MIXERCONTROLW));
1749 return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
1750 }
1751 }
1752 DPRINT1("DeviceInfo->u.MixControls.dwControlType %x not found in Line %x\n", DeviceInfo->u.MixControls.dwControlType, DeviceInfo->u.MixControls.dwLineID);
1753 return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, sizeof(WDMAUD_DEVICE_INFO));
1754 }
1755
1756 UNIMPLEMENTED;
1757 //DbgBreakPoint();
1758 return SetIrpIoStatus(Irp, STATUS_NOT_IMPLEMENTED, 0);
1759
1760 }
1761
1762 NTSTATUS
1763 NTAPI
1764 WdmAudSetControlDetails(
1765 IN PDEVICE_OBJECT DeviceObject,
1766 IN PIRP Irp,
1767 IN PWDMAUD_DEVICE_INFO DeviceInfo,
1768 IN PWDMAUD_CLIENT ClientInfo)
1769 {
1770 UNIMPLEMENTED;
1771 //DbgBreakPoint();
1772 return SetIrpIoStatus(Irp, STATUS_NOT_IMPLEMENTED, 0);
1773
1774 }
1775
1776 NTSTATUS
1777 NTAPI
1778 WdmAudGetControlDetails(
1779 IN PDEVICE_OBJECT DeviceObject,
1780 IN PIRP Irp,
1781 IN PWDMAUD_DEVICE_INFO DeviceInfo,
1782 IN PWDMAUD_CLIENT ClientInfo)
1783 {
1784 UNIMPLEMENTED;
1785 //DbgBreakPoint();
1786 return SetIrpIoStatus(Irp, STATUS_NOT_IMPLEMENTED, 0);
1787
1788 }
1789