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