[WDMAUD.DRV]
[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
14 ULONG
15 GetSysAudioDeviceCount(
16 IN PDEVICE_OBJECT DeviceObject)
17 {
18 PWDMAUD_DEVICE_EXTENSION DeviceExtension;
19 KSPROPERTY Pin;
20 ULONG Count, BytesReturned;
21 NTSTATUS Status;
22
23 /* setup the query request */
24 Pin.Set = KSPROPSETID_Sysaudio;
25 Pin.Id = KSPROPERTY_SYSAUDIO_DEVICE_COUNT;
26 Pin.Flags = KSPROPERTY_TYPE_GET;
27
28 DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
29
30 /* query sysaudio for the device count */
31 Status = KsSynchronousIoControlDevice(DeviceExtension->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSPROPERTY), (PVOID)&Count, sizeof(ULONG), &BytesReturned);
32 if (!NT_SUCCESS(Status))
33 return 0;
34
35 return Count;
36 }
37
38 NTSTATUS
39 GetSysAudioDevicePnpName(
40 IN PDEVICE_OBJECT DeviceObject,
41 IN ULONG DeviceIndex,
42 OUT LPWSTR * Device)
43 {
44 ULONG BytesReturned;
45 KSP_PIN Pin;
46 NTSTATUS Status;
47 PWDMAUD_DEVICE_EXTENSION DeviceExtension;
48
49 /* first check if the device index is within bounds */
50 if (DeviceIndex >= GetSysAudioDeviceCount(DeviceObject))
51 return STATUS_INVALID_PARAMETER;
52
53 /* setup the query request */
54 Pin.Property.Set = KSPROPSETID_Sysaudio;
55 Pin.Property.Id = KSPROPERTY_SYSAUDIO_DEVICE_INTERFACE_NAME;
56 Pin.Property.Flags = KSPROPERTY_TYPE_GET;
57 Pin.PinId = DeviceIndex;
58
59 DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
60
61 /* query sysaudio for the device path */
62 Status = KsSynchronousIoControlDevice(DeviceExtension->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSPROPERTY) + sizeof(ULONG), NULL, 0, &BytesReturned);
63
64 /* check if the request failed */
65 if (Status != STATUS_BUFFER_TOO_SMALL || BytesReturned == 0)
66 return STATUS_UNSUCCESSFUL;
67
68 /* allocate buffer for the device */
69 *Device = ExAllocatePool(NonPagedPool, BytesReturned);
70 if (!Device)
71 return STATUS_INSUFFICIENT_RESOURCES;
72
73 /* query sysaudio again for the device path */
74 Status = KsSynchronousIoControlDevice(DeviceExtension->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSPROPERTY) + sizeof(ULONG), (PVOID)*Device, BytesReturned, &BytesReturned);
75
76 if (!NT_SUCCESS(Status))
77 {
78 /* failed */
79 ExFreePool(*Device);
80 return Status;
81 }
82
83 return Status;
84 }
85
86 NTSTATUS
87 OpenSysAudioDeviceByIndex(
88 IN PDEVICE_OBJECT DeviceObject,
89 IN ULONG DeviceIndex,
90 IN PHANDLE DeviceHandle,
91 IN PFILE_OBJECT * FileObject)
92 {
93 LPWSTR Device = NULL;
94 NTSTATUS Status;
95 HANDLE hDevice;
96
97 Status = GetSysAudioDevicePnpName(DeviceObject, DeviceIndex, &Device);
98 if (!NT_SUCCESS(Status))
99 return Status;
100
101 /* now open the device */
102 Status = WdmAudOpenSysAudioDevice(Device, &hDevice);
103
104 /* free device buffer */
105 ExFreePool(Device);
106
107 if (!NT_SUCCESS(Status))
108 {
109 return Status;
110 }
111
112 *DeviceHandle = hDevice;
113
114 if (FileObject)
115 {
116 Status = ObReferenceObjectByHandle(hDevice, FILE_READ_DATA | FILE_WRITE_DATA, IoFileObjectType, KernelMode, (PVOID*)FileObject, NULL);
117
118 if (!NT_SUCCESS(Status))
119 {
120 ZwClose(hDevice);
121 }
122 }
123
124 return Status;
125 }
126
127 NTSTATUS
128 GetFilterNodeTypes(
129 PFILE_OBJECT FileObject,
130 PKSMULTIPLE_ITEM * Item)
131 {
132 NTSTATUS Status;
133 ULONG BytesReturned;
134 PKSMULTIPLE_ITEM MultipleItem;
135 KSPROPERTY Property;
136
137 /* setup query request */
138 Property.Id = KSPROPERTY_TOPOLOGY_NODES;
139 Property.Flags = KSPROPERTY_TYPE_GET;
140 Property.Set = KSPROPSETID_Topology;
141
142 /* query for required size */
143 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), NULL, 0, &BytesReturned);
144
145 /* check for success */
146 if (Status != STATUS_MORE_ENTRIES)
147 return Status;
148
149 /* allocate buffer */
150 MultipleItem = (PKSMULTIPLE_ITEM)ExAllocatePool(NonPagedPool, BytesReturned);
151 if (!MultipleItem)
152 return STATUS_INSUFFICIENT_RESOURCES;
153
154 /* query for required size */
155 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), (PVOID)MultipleItem, BytesReturned, &BytesReturned);
156
157 if (!NT_SUCCESS(Status))
158 {
159 /* failed */
160 ExFreePool(MultipleItem);
161 return Status;
162 }
163
164 *Item = MultipleItem;
165 return Status;
166 }
167
168 ULONG
169 CountNodeType(
170 PKSMULTIPLE_ITEM MultipleItem,
171 LPGUID NodeType)
172 {
173 ULONG Count;
174 ULONG Index;
175 LPGUID Guid;
176
177 Count = 0;
178 Guid = (LPGUID)(MultipleItem+1);
179
180 /* iterate through node type array */
181 for(Index = 0; Index < MultipleItem->Count; Index++)
182 {
183 if (IsEqualGUIDAligned(NodeType, Guid))
184 {
185 /* found matching guid */
186 Count++;
187 }
188 Guid++;
189 }
190 return Count;
191 }
192
193 ULONG
194 GetNumOfMixerDevices(
195 IN PDEVICE_OBJECT DeviceObject)
196 {
197 ULONG DeviceCount, Index, Count;
198 NTSTATUS Status;
199 HANDLE hDevice;
200 PFILE_OBJECT FileObject;
201 PKSMULTIPLE_ITEM MultipleItem;
202
203 /* get number of devices */
204 DeviceCount = GetSysAudioDeviceCount(DeviceObject);
205
206 if (!DeviceCount)
207 return 0;
208
209 Index = 0;
210 Count = 0;
211 do
212 {
213 /* open the virtual audio device */
214 Status = OpenSysAudioDeviceByIndex(DeviceObject, Index, &hDevice, &FileObject);
215
216 if (NT_SUCCESS(Status))
217 {
218 /* retrieve all available node types */
219 Status = GetFilterNodeTypes(FileObject, &MultipleItem);
220 if (NT_SUCCESS(Status))
221 {
222 if (CountNodeType(MultipleItem, (LPGUID)&KSNODETYPE_DAC))
223 {
224 /* increment (output) mixer count */
225 Count++;
226 }
227
228 if (CountNodeType(MultipleItem, (LPGUID)&KSNODETYPE_ADC))
229 {
230 /* increment (input) mixer count */
231 Count++;
232 }
233 ExFreePool(MultipleItem);
234 }
235 ObDereferenceObject(FileObject);
236 ZwClose(hDevice);
237 }
238
239 Index++;
240 }while(Index < DeviceCount);
241
242 return Count;
243 }
244
245 ULONG
246 IsOutputMixer(
247 IN PDEVICE_OBJECT DeviceObject,
248 IN ULONG DeviceIndex)
249 {
250 ULONG DeviceCount, Index, Count;
251 NTSTATUS Status;
252 HANDLE hDevice;
253 PFILE_OBJECT FileObject;
254 PKSMULTIPLE_ITEM MultipleItem;
255
256 /* get number of devices */
257 DeviceCount = GetSysAudioDeviceCount(DeviceObject);
258
259 if (!DeviceCount)
260 return 0;
261
262 Index = 0;
263 Count = 0;
264 do
265 {
266 /* open the virtual audio device */
267 Status = OpenSysAudioDeviceByIndex(DeviceObject, Index, &hDevice, &FileObject);
268
269 if (NT_SUCCESS(Status))
270 {
271 /* retrieve all available node types */
272 Status = GetFilterNodeTypes(FileObject, &MultipleItem);
273 if (NT_SUCCESS(Status))
274 {
275 if (CountNodeType(MultipleItem, (LPGUID)&KSNODETYPE_DAC))
276 {
277 /* increment (output) mixer count */
278 if (DeviceIndex == Count)
279 {
280 ExFreePool(MultipleItem);
281 ObDereferenceObject(FileObject);
282 ZwClose(hDevice);
283 return TRUE;
284 }
285
286 Count++;
287 }
288
289 if (CountNodeType(MultipleItem, (LPGUID)&KSNODETYPE_ADC))
290 {
291 /* increment (input) mixer count */
292 if (DeviceIndex == Count)
293 {
294 ExFreePool(MultipleItem);
295 ObDereferenceObject(FileObject);
296 ZwClose(hDevice);
297 return FALSE;
298 }
299 Count++;
300 }
301 ExFreePool(MultipleItem);
302 }
303 ObDereferenceObject(FileObject);
304 ZwClose(hDevice);
305 }
306
307 Index++;
308 }while(Index < DeviceCount);
309
310 ASSERT(0);
311 return FALSE;
312 }
313
314
315
316
317 NTSTATUS
318 WdmAudMixerCapabilities(
319 IN PDEVICE_OBJECT DeviceObject,
320 IN PWDMAUD_DEVICE_INFO DeviceInfo,
321 IN PWDMAUD_CLIENT ClientInfo)
322 {
323 NTSTATUS Status;
324 LPWSTR Device;
325 WCHAR Buffer[100];
326
327 Status = GetSysAudioDevicePnpName(DeviceObject, DeviceInfo->DeviceIndex,&Device);
328 if (!NT_SUCCESS(Status))
329 {
330 DPRINT1("Failed to get device name %x\n", Status);
331 return Status;
332 }
333
334 DeviceInfo->u.MixCaps.cDestinations = 1; //FIXME
335
336 Status = FindProductName(Device, sizeof(Buffer) / sizeof(WCHAR), Buffer);
337
338 /* check for success */
339 if (!NT_SUCCESS(Status))
340 {
341 DeviceInfo->u.MixCaps.szPname[0] = L'\0';
342 }
343 else
344 {
345 if (IsOutputMixer(DeviceObject, DeviceInfo->DeviceIndex))
346 {
347 wcscat(Buffer, L" output");
348 }
349 else
350 {
351 wcscat(Buffer, L" Input");
352 }
353 RtlMoveMemory(DeviceInfo->u.MixCaps.szPname, Buffer, min(MAXPNAMELEN, wcslen(Buffer)+1) * sizeof(WCHAR));
354 DeviceInfo->u.MixCaps.szPname[MAXPNAMELEN-1] = L'\0';
355 }
356
357 return Status;
358 }
359
360
361 NTSTATUS
362 WdmAudControlOpenMixer(
363 IN PDEVICE_OBJECT DeviceObject,
364 IN PIRP Irp,
365 IN PWDMAUD_DEVICE_INFO DeviceInfo,
366 IN PWDMAUD_CLIENT ClientInfo)
367 {
368 ULONG Index;
369 PWDMAUD_HANDLE Handels;
370
371 DPRINT("WdmAudControlOpenMixer\n");
372
373 if (DeviceInfo->DeviceIndex >= GetNumOfMixerDevices(DeviceObject))
374 {
375 /* mixer index doesnt exist */
376 return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, 0);
377 }
378
379 for(Index = 0; Index < ClientInfo->NumPins; Index++)
380 {
381 if (ClientInfo->hPins[Index].Handle == (HANDLE)DeviceInfo->DeviceIndex && ClientInfo->hPins[Index].Type == MIXER_DEVICE_TYPE)
382 {
383 /* re-use pseudo handle */
384 DeviceInfo->hDevice = (HANDLE)DeviceInfo->DeviceIndex;
385 return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
386 }
387 }
388
389 Handels = ExAllocatePool(NonPagedPool, sizeof(WDMAUD_HANDLE) * (ClientInfo->NumPins+1));
390
391 if (Handels)
392 {
393 if (ClientInfo->NumPins)
394 {
395 RtlMoveMemory(Handels, ClientInfo->hPins, sizeof(WDMAUD_HANDLE) * ClientInfo->NumPins);
396 ExFreePool(ClientInfo->hPins);
397 }
398
399 ClientInfo->hPins = Handels;
400 ClientInfo->hPins[ClientInfo->NumPins].Handle = (HANDLE)DeviceInfo->DeviceIndex;
401 ClientInfo->hPins[ClientInfo->NumPins].Type = MIXER_DEVICE_TYPE;
402 ClientInfo->NumPins++;
403 }
404 else
405 {
406 return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, sizeof(WDMAUD_DEVICE_INFO));
407 }
408 DeviceInfo->hDevice = (HANDLE)DeviceInfo->DeviceIndex;
409
410 return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
411 }
412