9932d093c55e7a09d1d80ba98a0e2cec0c72d4ce
[reactos.git] / reactos / drivers / wdm / audio / sysaudio / control.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: drivers/wdm/audio/sysaudio/control.c
5 * PURPOSE: System Audio graph builder
6 * PROGRAMMER: Johannes Anderwald
7 */
8
9 #include "sysaudio.h"
10
11 #define NDEBUG
12 #include <debug.h>
13
14 const GUID KSPROPSETID_Sysaudio = {0xCBE3FAA0L, 0xCC75, 0x11D0, {0xB4, 0x65, 0x00, 0x00, 0x1A, 0x18, 0x18, 0xE6}};
15 const GUID KSPROPSETID_Sysaudio_Pin = {0xA3A53220L, 0xC6E4, 0x11D0, {0xB4, 0x65, 0x00, 0x00, 0x1A, 0x18, 0x18, 0xE6}};
16 const GUID KSPROPSETID_General = {0x1464EDA5L, 0x6A8F, 0x11D1, {0x9A, 0xA7, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
17 const GUID KSPROPSETID_Pin = {0x8C134960L, 0x51AD, 0x11CF, {0x87, 0x8A, 0x94, 0xF8, 0x01, 0xC1, 0x00, 0x00}};
18 const GUID KSPROPSETID_Connection = {0x1D58C920L, 0xAC9B, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
19 const GUID KSPROPSETID_Topology = {0x720D4AC0L, 0x7533, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
20 const GUID KSDATAFORMAT_TYPE_AUDIO = {0x73647561L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
21 const GUID KSDATAFORMAT_SUBTYPE_PCM = {0x00000001L, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
22 const GUID KSDATAFORMAT_SPECIFIER_WAVEFORMATEX = {0x05589f81L, 0xc356, 0x11ce, {0xbf, 0x01, 0x00, 0xaa, 0x00, 0x55, 0x59, 0x5a}};
23
24 NTSTATUS
25 SetIrpIoStatus(
26 IN PIRP Irp,
27 IN NTSTATUS Status,
28 IN ULONG Length)
29 {
30 Irp->IoStatus.Information = Length;
31 Irp->IoStatus.Status = Status;
32 if (Status != STATUS_PENDING)
33 {
34 IoCompleteRequest(Irp, IO_NO_INCREMENT);
35 }
36 else
37 {
38 IoMarkIrpPending(Irp);
39 }
40 return Status;
41
42 }
43
44 PKSAUDIO_DEVICE_ENTRY
45 GetListEntry(
46 IN PLIST_ENTRY Head,
47 IN ULONG Index)
48 {
49 PLIST_ENTRY Entry = Head->Flink;
50
51 while(Index-- && Entry != Head)
52 Entry = Entry->Flink;
53
54 if (Entry == Head)
55 return NULL;
56
57 return (PKSAUDIO_DEVICE_ENTRY)CONTAINING_RECORD(Entry, KSAUDIO_DEVICE_ENTRY, Entry);
58 }
59
60 NTSTATUS
61 SysAudioOpenVirtualDevice(
62 IN PIRP Irp,
63 IN ULONG DeviceNumber,
64 PSYSAUDIODEVEXT DeviceExtension)
65 {
66 PKSAUDIO_DEVICE_ENTRY Entry;
67 PIO_STACK_LOCATION IoStack;
68
69 /* get current irp stack */
70 IoStack = IoGetCurrentIrpStackLocation(Irp);
71
72 /* sanity check */
73 ASSERT(IoStack->FileObject);
74
75 if (DeviceNumber >= DeviceExtension->NumberOfKsAudioDevices)
76 {
77 /* invalid device index */
78 return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
79 }
80
81 /* get device context */
82 Entry = GetListEntry(&DeviceExtension->KsAudioDeviceList, DeviceNumber);
83 ASSERT(Entry != NULL);
84
85 /* store device entry in FsContext
86 * see pin.c DispatchCreateSysAudioPin for details
87 */
88 IoStack->FileObject->FsContext = (PVOID)Entry;
89
90 return SetIrpIoStatus(Irp, STATUS_SUCCESS, 0);
91 }
92
93 NTSTATUS
94 HandleSysAudioFilterPinProperties(
95 PIRP Irp,
96 PKSPROPERTY Property,
97 PSYSAUDIODEVEXT DeviceExtension)
98 {
99 PIO_STACK_LOCATION IoStack;
100 NTSTATUS Status;
101 PKSAUDIO_DEVICE_ENTRY Entry;
102 ULONG BytesReturned;
103
104 // in order to access pin properties of a sysaudio device
105 // the caller must provide a KSP_PIN struct, where
106 // Reserved member points to virtual device index
107
108 IoStack = IoGetCurrentIrpStackLocation(Irp);
109 if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSP_PIN))
110 {
111 /* too small buffer */
112 return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(KSPROPERTY) + sizeof(ULONG));
113 }
114
115 Entry = GetListEntry(&DeviceExtension->KsAudioDeviceList, ((KSP_PIN*)Property)->Reserved);
116 if (!Entry)
117 {
118 /* invalid device index */
119 return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
120 }
121
122 /* forward request to the filter implementing the property */
123 Status = KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY,
124 (PVOID)IoStack->Parameters.DeviceIoControl.Type3InputBuffer,
125 IoStack->Parameters.DeviceIoControl.InputBufferLength,
126 Irp->UserBuffer,
127 IoStack->Parameters.DeviceIoControl.OutputBufferLength,
128 &BytesReturned);
129
130 return SetIrpIoStatus(Irp, Status, BytesReturned);
131 }
132
133
134 NTSTATUS
135 ComputeCompatibleFormat(
136 IN PKSAUDIO_DEVICE_ENTRY Entry,
137 IN ULONG PinId,
138 IN PKSDATAFORMAT_WAVEFORMATEX ClientFormat,
139 OUT PKSDATAFORMAT_WAVEFORMATEX MixerFormat)
140 {
141 BOOL bFound;
142 ULONG BytesReturned;
143 PKSP_PIN PinRequest;
144 NTSTATUS Status;
145 PKSMULTIPLE_ITEM MultipleItem;
146 ULONG Length;
147 PKSDATARANGE_AUDIO AudioRange;
148 ULONG Index;
149
150 Length = sizeof(KSP_PIN) + sizeof(KSMULTIPLE_ITEM) + ClientFormat->DataFormat.FormatSize;
151 PinRequest = AllocateItem(NonPagedPool, Length);
152 if (!PinRequest)
153 return STATUS_UNSUCCESSFUL;
154
155 PinRequest->PinId = PinId;
156 PinRequest->Property.Set = KSPROPSETID_Pin;
157 PinRequest->Property.Flags = KSPROPERTY_TYPE_GET;
158 PinRequest->Property.Id = KSPROPERTY_PIN_DATAINTERSECTION;
159
160 MultipleItem = (PKSMULTIPLE_ITEM)(PinRequest + 1);
161 MultipleItem->Count = 1;
162 MultipleItem->Size = ClientFormat->DataFormat.FormatSize;
163
164 RtlMoveMemory(MultipleItem + 1, ClientFormat, ClientFormat->DataFormat.FormatSize);
165 /* Query the miniport data intersection handler */
166 Status = KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)PinRequest, Length, (PVOID)MixerFormat, sizeof(KSDATAFORMAT_WAVEFORMATEX), &BytesReturned);
167
168 DPRINT("Status %x\n", Status);
169
170 if (NT_SUCCESS(Status))
171 {
172 FreeItem(PinRequest);
173 return Status;
174 }
175
176 /* Setup request block */
177 PinRequest->Property.Id = KSPROPERTY_PIN_DATARANGES;
178 /* Query pin data ranges */
179 Status = KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)PinRequest, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
180
181 if (Status != STATUS_MORE_ENTRIES)
182 {
183 /* Failed to get data ranges */
184 return Status;
185 }
186
187 MultipleItem = AllocateItem(NonPagedPool, BytesReturned);
188 if (!MultipleItem)
189 {
190 FreeItem(PinRequest);
191 return STATUS_NO_MEMORY;
192 }
193
194 Status = KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)PinRequest, sizeof(KSP_PIN), (PVOID)MultipleItem, BytesReturned, &BytesReturned);
195 if (!NT_SUCCESS(Status))
196 {
197 DPRINT("Property Request KSPROPERTY_PIN_DATARANGES failed with %x\n", Status);
198 FreeItem(MultipleItem);
199 FreeItem(PinRequest);
200 return STATUS_UNSUCCESSFUL;
201 }
202
203 AudioRange = (PKSDATARANGE_AUDIO)(MultipleItem + 1);
204 bFound = FALSE;
205 for(Index = 0; Index < MultipleItem->Count; Index++)
206 {
207 if (AudioRange->DataRange.FormatSize != sizeof(KSDATARANGE_AUDIO))
208 {
209 UNIMPLEMENTED
210 AudioRange = (PKSDATARANGE_AUDIO)((PUCHAR)AudioRange + AudioRange->DataRange.FormatSize);
211 continue;
212 }
213 /* Select best quality available */
214
215 MixerFormat->DataFormat.FormatSize = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATEX);
216 MixerFormat->DataFormat.Flags = 0;
217 MixerFormat->DataFormat.Reserved = 0;
218 MixerFormat->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
219 MixerFormat->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
220 MixerFormat->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
221 MixerFormat->DataFormat.SampleSize = 4;
222 MixerFormat->WaveFormatEx.wFormatTag = ClientFormat->WaveFormatEx.wFormatTag;
223 #ifndef NO_AC97_HACK
224 /* HACK: AC97 does not support mono render / record */
225 MixerFormat->WaveFormatEx.nChannels = 2;
226 /*HACK: AC97 only supports 16-Bit Bits */
227 MixerFormat->WaveFormatEx.wBitsPerSample = 16;
228
229 #else
230 MixerFormat->WaveFormatEx.nChannels = min(ClientFormat->WaveFormatEx.nChannels, AudioRange->MaximumChannels);
231 MixerFormat->WaveFormatEx.wBitsPerSample = AudioRange->MaximumBitsPerSample;
232 #endif
233
234 #ifdef KMIXER_RESAMPLING_IMPLEMENTED
235 MixerFormat->WaveFormatEx.nSamplesPerSec = AudioRange->MaximumSampleFrequency;
236 #else
237 MixerFormat->WaveFormatEx.nSamplesPerSec = max(AudioRange->MinimumSampleFrequency, min(ClientFormat->WaveFormatEx.nSamplesPerSec, AudioRange->MaximumSampleFrequency));
238 #endif
239
240 MixerFormat->WaveFormatEx.cbSize = 0;
241 MixerFormat->WaveFormatEx.nBlockAlign = (MixerFormat->WaveFormatEx.nChannels * MixerFormat->WaveFormatEx.wBitsPerSample) / 8;
242 MixerFormat->WaveFormatEx.nAvgBytesPerSec = MixerFormat->WaveFormatEx.nChannels * MixerFormat->WaveFormatEx.nSamplesPerSec * (MixerFormat->WaveFormatEx.wBitsPerSample / 8);
243
244 bFound = TRUE;
245 break;
246
247 AudioRange = (PKSDATARANGE_AUDIO)((PUCHAR)AudioRange + AudioRange->DataRange.FormatSize);
248 }
249
250 #if 0
251 DPRINT1("\nNum Max Channels %u Channels %u Old Channels %u\n Max SampleRate %u SampleRate %u Old SampleRate %u\n Max BitsPerSample %u BitsPerSample %u Old BitsPerSample %u\n",
252 AudioRange->MaximumChannels, MixerFormat->WaveFormatEx.nChannels, ClientFormat->WaveFormatEx.nChannels,
253 AudioRange->MaximumSampleFrequency, MixerFormat->WaveFormatEx.nSamplesPerSec, ClientFormat->WaveFormatEx.nSamplesPerSec,
254 AudioRange->MaximumBitsPerSample, MixerFormat->WaveFormatEx.wBitsPerSample, ClientFormat->WaveFormatEx.wBitsPerSample);
255
256
257 #endif
258
259 FreeItem(MultipleItem);
260 FreeItem(PinRequest);
261
262 if (bFound)
263 return STATUS_SUCCESS;
264 else
265 return STATUS_NOT_IMPLEMENTED;
266 }
267
268 NTSTATUS
269 GetPinInstanceCount(
270 PKSAUDIO_DEVICE_ENTRY Entry,
271 PKSPIN_CINSTANCES PinInstances,
272 PKSPIN_CONNECT PinConnect)
273 {
274 KSP_PIN PinRequest;
275 ULONG BytesReturned;
276
277 /* query the instance count */
278 PinRequest.PinId = PinConnect->PinId;
279 PinRequest.Property.Set = KSPROPSETID_Pin;
280 PinRequest.Property.Flags = KSPROPERTY_TYPE_GET;
281 PinRequest.Property.Id = KSPROPERTY_PIN_CINSTANCES;
282 ASSERT(Entry->FileObject);
283 return KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&PinRequest, sizeof(KSP_PIN), (PVOID)PinInstances, sizeof(KSPIN_CINSTANCES), &BytesReturned);
284
285 }
286
287 NTSTATUS
288 SysAudioHandleProperty(
289 PDEVICE_OBJECT DeviceObject,
290 PIRP Irp)
291 {
292 PIO_STACK_LOCATION IoStack;
293 NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
294 KSPROPERTY PropertyRequest;
295 KSCOMPONENTID ComponentId;
296 PULONG Index;
297 PKSPROPERTY Property;
298 PSYSAUDIODEVEXT DeviceExtension;
299 PKSAUDIO_DEVICE_ENTRY Entry;
300 PSYSAUDIO_INSTANCE_INFO InstanceInfo;
301 ULONG BytesReturned;
302 UNICODE_STRING GuidString;
303 PKSP_PIN Pin;
304 LPWSTR DeviceName;
305
306 IoStack = IoGetCurrentIrpStackLocation(Irp);
307
308 if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSPROPERTY))
309 {
310 /* buffer must be at least of sizeof KSPROPERTY */
311 return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(KSPROPERTY));
312 }
313
314 Property = (PKSPROPERTY)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
315 DeviceExtension = (PSYSAUDIODEVEXT)DeviceObject->DeviceExtension;
316
317 if (IsEqualGUIDAligned(&Property->Set, &KSPROPSETID_Pin))
318 {
319 return HandleSysAudioFilterPinProperties(Irp, Property, DeviceExtension);
320 }
321 else if(IsEqualGUIDAligned(&Property->Set, &KSPROPSETID_Topology))
322 {
323 if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSP_PIN))
324 {
325 /* too small buffer */
326 return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(KSP_PIN));
327 }
328 Pin = (PKSP_PIN)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
329 Entry = GetListEntry(&DeviceExtension->KsAudioDeviceList, Pin->Reserved);
330 ASSERT(Entry != NULL);
331
332 /* forward request to the filter implementing the property */
333 Status = KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY,
334 (PVOID)IoStack->Parameters.DeviceIoControl.Type3InputBuffer,
335 IoStack->Parameters.DeviceIoControl.InputBufferLength,
336 Irp->UserBuffer,
337 IoStack->Parameters.DeviceIoControl.OutputBufferLength,
338 &BytesReturned);
339
340 return SetIrpIoStatus(Irp, Status, BytesReturned);
341 }
342 else if (IsEqualGUIDAligned(&Property->Set, &KSPROPSETID_Sysaudio))
343 {
344 if (Property->Id == KSPROPERTY_SYSAUDIO_DEVICE_INTERFACE_NAME)
345 {
346 if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSPROPERTY) + sizeof(ULONG))
347 {
348 /* invalid request */
349 return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, sizeof(KSPROPERTY) + sizeof(ULONG));
350 }
351 Index = (PULONG)(Property + 1);
352
353 if (DeviceExtension->NumberOfKsAudioDevices <= *Index)
354 {
355 /* invalid index */
356 return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
357 }
358
359 Entry = GetListEntry(&DeviceExtension->KsAudioDeviceList, *Index);
360 ASSERT(Entry != NULL);
361
362 BytesReturned = Entry->DeviceName.Length + sizeof(WCHAR);
363 if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < BytesReturned)
364 {
365 /* too small buffer */
366 return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, BytesReturned);
367 }
368
369 /* copy device name */
370 DeviceName = (LPWSTR)Irp->UserBuffer;
371
372 RtlMoveMemory(DeviceName, Entry->DeviceName.Buffer, Entry->DeviceName.Length);
373 DeviceName[Entry->DeviceName.Length / sizeof(WCHAR)] = L'\0';
374 return SetIrpIoStatus(Irp, STATUS_SUCCESS, BytesReturned);
375 }
376
377 if (Property->Id == KSPROPERTY_SYSAUDIO_COMPONENT_ID)
378 {
379 if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(KSPROPERTY) + sizeof(ULONG))
380 {
381 /* too small buffer */
382 return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(KSPROPERTY) + sizeof(ULONG));
383 }
384
385 if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(KSCOMPONENTID))
386 {
387 /* too small buffer */
388 return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(KSCOMPONENTID));
389 }
390
391 Index = (PULONG)(Property + 1);
392
393 if (DeviceExtension->NumberOfKsAudioDevices <= *Index)
394 {
395 /* invalid index */
396 return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
397 }
398 Entry = GetListEntry(&DeviceExtension->KsAudioDeviceList, *Index);
399 ASSERT(Entry != NULL);
400
401 PropertyRequest.Set = KSPROPSETID_General;
402 PropertyRequest.Id = KSPROPERTY_GENERAL_COMPONENTID;
403 PropertyRequest.Flags = KSPROPERTY_TYPE_GET;
404
405 /* call the filter */
406 Status = KsSynchronousIoControlDevice(Entry->FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&PropertyRequest, sizeof(KSPROPERTY), (PVOID)&ComponentId, sizeof(KSCOMPONENTID), &BytesReturned);
407 if (!NT_SUCCESS(Status))
408 {
409 DPRINT("KsSynchronousIoControlDevice failed with %x for KSPROPERTY_GENERAL_COMPONENTID\n", Status);
410 return SetIrpIoStatus(Irp, Status, 0);
411 }
412 RtlMoveMemory(Irp->UserBuffer, &ComponentId, sizeof(KSCOMPONENTID));
413 return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(KSCOMPONENTID));
414 }
415 else if (Property->Id == KSPROPERTY_SYSAUDIO_DEVICE_COUNT)
416 {
417 if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
418 {
419 /* too small buffer */
420 return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(ULONG));
421 }
422
423 *((PULONG)Irp->UserBuffer) = DeviceExtension->NumberOfKsAudioDevices;
424 return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(ULONG));
425 }
426 else if (Property->Id == KSPROPERTY_SYSAUDIO_DEVICE_INSTANCE)
427 {
428 if (IoStack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG))
429 {
430 /* too small buffer */
431 return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(ULONG));
432 }
433
434 if (Property->Flags & KSPROPERTY_TYPE_SET)
435 {
436 Index = (PULONG)Irp->UserBuffer;
437 return SysAudioOpenVirtualDevice(Irp, *Index, DeviceExtension);
438 }
439 }
440 else if (Property->Id == KSPROPERTY_SYSAUDIO_INSTANCE_INFO)
441 {
442 if (IoStack->Parameters.DeviceIoControl.InputBufferLength < sizeof(SYSAUDIO_INSTANCE_INFO))
443 {
444 /* too small buffer */
445 return SetIrpIoStatus(Irp, STATUS_BUFFER_TOO_SMALL, sizeof(SYSAUDIO_INSTANCE_INFO));
446 }
447
448 /* get input parameter */
449 InstanceInfo = (PSYSAUDIO_INSTANCE_INFO)Property;
450
451 if (Property->Flags & KSPROPERTY_TYPE_SET)
452 {
453 return SysAudioOpenVirtualDevice(Irp, InstanceInfo->DeviceNumber, DeviceExtension);
454 }
455 }
456 }
457
458 RtlStringFromGUID(&Property->Set, &GuidString);
459 DPRINT1("Unhandled property Set |%S| Id %u Flags %x\n", GuidString.Buffer, Property->Id, Property->Flags);
460 RtlFreeUnicodeString(&GuidString);
461 return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, 0);
462 }