e6d88eacaef3307824cded89a888f3c1d8674bb2
[reactos.git] / reactos / drivers / wdm / audio / sysaudio / pin.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: drivers/wdm/audio/sysaudio/deviface.c
5 * PURPOSE: System Audio graph builder
6 * PROGRAMMER: Johannes Anderwald
7 */
8
9 #include "sysaudio.h"
10
11 NTSTATUS
12 NTAPI
13 Pin_fnDeviceIoControl(
14 PDEVICE_OBJECT DeviceObject,
15 PIRP Irp)
16 {
17 PDISPATCH_CONTEXT Context;
18 NTSTATUS Status;
19 ULONG BytesReturned;
20 PFILE_OBJECT FileObject;
21 PIO_STACK_LOCATION IoStack;
22
23 DPRINT("Pin_fnDeviceIoControl called DeviceObject %p Irp %p\n", DeviceObject);
24
25 /* Get current stack location */
26 IoStack = IoGetCurrentIrpStackLocation(Irp);
27
28 /* The dispatch context is stored in the FsContext member */
29 Context = (PDISPATCH_CONTEXT)IoStack->FileObject->FsContext;
30
31 /* Sanity check */
32 ASSERT(Context);
33
34 /* acquire real pin file object */
35 Status = ObReferenceObjectByHandle(Context->Handle, GENERIC_WRITE, IoFileObjectType, KernelMode, (PVOID*)&FileObject, NULL);
36 if (!NT_SUCCESS(Status))
37 {
38 Irp->IoStatus.Information = 0;
39 Irp->IoStatus.Status = Status;
40 /* Complete the irp */
41 IoCompleteRequest(Irp, IO_NO_INCREMENT);
42 return Status;
43 }
44
45 /* Re-dispatch the request to the real target pin */
46 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IoStack->Parameters.DeviceIoControl.IoControlCode,
47 IoStack->Parameters.DeviceIoControl.Type3InputBuffer,
48 IoStack->Parameters.DeviceIoControl.InputBufferLength,
49 Irp->UserBuffer,
50 IoStack->Parameters.DeviceIoControl.OutputBufferLength,
51 &BytesReturned);
52 /* release file object */
53 ObDereferenceObject(FileObject);
54
55 /* Save status and information */
56 Irp->IoStatus.Information = BytesReturned;
57 Irp->IoStatus.Status = Status;
58 /* Complete the irp */
59 IoCompleteRequest(Irp, IO_NO_INCREMENT);
60 /* Done */
61 return Status;
62 }
63
64
65
66 NTSTATUS
67 NTAPI
68 Pin_fnWrite(
69 PDEVICE_OBJECT DeviceObject,
70 PIRP Irp)
71 {
72 PDISPATCH_CONTEXT Context;
73 PIO_STACK_LOCATION IoStack;
74 PFILE_OBJECT FileObject;
75 NTSTATUS Status;
76 ULONG Length;
77
78 /* Get current stack location */
79 IoStack = IoGetCurrentIrpStackLocation(Irp);
80
81 Length = IoStack->Parameters.Write.Length;
82
83 /* The dispatch context is stored in the FsContext member */
84 Context = (PDISPATCH_CONTEXT)IoStack->FileObject->FsContext;
85
86 /* Sanity check */
87 ASSERT(Context);
88
89 if (Context->hMixerPin)
90 {
91 // FIXME
92 // call kmixer to convert stream
93 UNIMPLEMENTED
94 }
95
96 /* acquire real pin file object */
97 Status = ObReferenceObjectByHandle(Context->Handle, GENERIC_WRITE, IoFileObjectType, KernelMode, (PVOID*)&FileObject, NULL);
98 if (!NT_SUCCESS(Status))
99 {
100 DPRINT1("failed\n");
101 Irp->IoStatus.Information = 0;
102 Irp->IoStatus.Status = Status;
103 /* Complete the irp */
104 IoCompleteRequest(Irp, IO_NO_INCREMENT);
105 return Status;
106 }
107
108 /* skip current irp location */
109 IoSkipCurrentIrpStackLocation(Irp);
110
111 /* get next stack location */
112 IoStack = IoGetNextIrpStackLocation(Irp);
113 /* store file object of next device object */
114 IoStack->FileObject = FileObject;
115 IoStack->MajorFunction = IRP_MJ_DEVICE_CONTROL;
116 IoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_KS_WRITE_STREAM; //FIXME
117 IoStack->Parameters.DeviceIoControl.OutputBufferLength = Length;
118
119 /* now call the driver */
120 return IoCallDriver(IoGetRelatedDeviceObject(FileObject), Irp);
121 }
122
123 NTSTATUS
124 NTAPI
125 Pin_fnClose(
126 PDEVICE_OBJECT DeviceObject,
127 PIRP Irp)
128 {
129 PDISPATCH_CONTEXT Context;
130 PIO_STACK_LOCATION IoStack;
131
132 DPRINT("Pin_fnClose called DeviceObject %p Irp %p\n", DeviceObject);
133
134 /* Get current stack location */
135 IoStack = IoGetCurrentIrpStackLocation(Irp);
136
137 /* The dispatch context is stored in the FsContext member */
138 Context = (PDISPATCH_CONTEXT)IoStack->FileObject->FsContext;
139
140 if (Context->Handle)
141 {
142 ZwClose(Context->Handle);
143 }
144 ZwClose(Context->hMixerPin);
145
146 ExFreePool(Context);
147
148 Irp->IoStatus.Status = STATUS_SUCCESS;
149 Irp->IoStatus.Information = 0;
150 IoCompleteRequest(Irp, IO_NO_INCREMENT);
151 return STATUS_SUCCESS;
152 }
153
154 BOOLEAN
155 NTAPI
156 Pin_fnFastWrite(
157 PFILE_OBJECT FileObject,
158 PLARGE_INTEGER FileOffset,
159 ULONG Length,
160 BOOLEAN Wait,
161 ULONG LockKey,
162 PVOID Buffer,
163 PIO_STATUS_BLOCK IoStatus,
164 PDEVICE_OBJECT DeviceObject)
165 {
166 PDISPATCH_CONTEXT Context;
167 PFILE_OBJECT RealFileObject;
168 NTSTATUS Status;
169
170 DPRINT("Pin_fnFastWrite called DeviceObject %p Irp %p\n", DeviceObject);
171
172 Context = (PDISPATCH_CONTEXT)FileObject->FsContext;
173
174 if (Context->hMixerPin)
175 {
176 Status = ObReferenceObjectByHandle(Context->hMixerPin, GENERIC_WRITE, IoFileObjectType, KernelMode, (PVOID*)&RealFileObject, NULL);
177 if (NT_SUCCESS(Status))
178 {
179 Status = KsStreamIo(RealFileObject, NULL, NULL, NULL, NULL, 0, IoStatus, Buffer, Length, KSSTREAM_WRITE, UserMode);
180 ObDereferenceObject(RealFileObject);
181 }
182
183 if (!NT_SUCCESS(Status))
184 {
185 DPRINT1("Mixing stream failed with %lx\n", Status);
186 DbgBreakPoint();
187 return FALSE;
188 }
189 }
190
191 Status = ObReferenceObjectByHandle(Context->Handle, GENERIC_WRITE, IoFileObjectType, KernelMode, (PVOID*)&RealFileObject, NULL);
192 if (!NT_SUCCESS(Status))
193 return FALSE;
194
195 Status = KsStreamIo(RealFileObject, NULL, NULL, NULL, NULL, 0, IoStatus, Buffer, Length, KSSTREAM_WRITE, UserMode);
196
197 ObDereferenceObject(RealFileObject);
198
199 if (NT_SUCCESS(Status))
200 return TRUE;
201 else
202 return FALSE;
203 }
204
205 static KSDISPATCH_TABLE PinTable =
206 {
207 Pin_fnDeviceIoControl,
208 KsDispatchInvalidDeviceRequest,
209 Pin_fnWrite,
210 KsDispatchInvalidDeviceRequest,
211 Pin_fnClose,
212 KsDispatchInvalidDeviceRequest,
213 KsDispatchInvalidDeviceRequest,
214 KsDispatchFastIoDeviceControlFailure,
215 KsDispatchFastReadFailure,
216 Pin_fnFastWrite,
217 };
218
219 NTSTATUS
220 SetMixerInputOutputFormat(
221 IN PFILE_OBJECT FileObject,
222 IN PKSDATAFORMAT InputFormat,
223 IN PKSDATAFORMAT OutputFormat)
224 {
225 KSP_PIN PinRequest;
226 ULONG BytesReturned;
227 NTSTATUS Status;
228
229 /* re-using pin */
230 PinRequest.Property.Set = KSPROPSETID_Connection;
231 PinRequest.Property.Flags = KSPROPERTY_TYPE_SET;
232 PinRequest.Property.Id = KSPROPERTY_CONNECTION_DATAFORMAT;
233
234 /* set the input format */
235 PinRequest.PinId = 0;
236 DPRINT("InputFormat %p Size %u WaveFormatSize %u DataFormat %u WaveEx %u\n", InputFormat, InputFormat->FormatSize, sizeof(KSDATAFORMAT_WAVEFORMATEX), sizeof(KSDATAFORMAT), sizeof(WAVEFORMATEX));
237 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY,
238 (PVOID)&PinRequest,
239 sizeof(KSP_PIN),
240 (PVOID)InputFormat,
241 InputFormat->FormatSize,
242 &BytesReturned);
243 if (!NT_SUCCESS(Status))
244 return Status;
245
246 /* set the the output format */
247 PinRequest.PinId = 1;
248 DPRINT("OutputFormat %p Size %u WaveFormatSize %u DataFormat %u WaveEx %u\n", OutputFormat, OutputFormat->FormatSize, sizeof(KSDATAFORMAT_WAVEFORMATEX), sizeof(KSDATAFORMAT), sizeof(WAVEFORMATEX));
249 Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY,
250 (PVOID)&PinRequest,
251 sizeof(KSP_PIN),
252 (PVOID)OutputFormat,
253 OutputFormat->FormatSize,
254 &BytesReturned);
255 return Status;
256 }
257
258
259 NTSTATUS
260 CreateMixerPinAndSetFormat(
261 IN HANDLE KMixerHandle,
262 IN KSPIN_CONNECT *PinConnect,
263 IN PKSDATAFORMAT InputFormat,
264 IN PKSDATAFORMAT OutputFormat,
265 OUT PHANDLE MixerPinHandle)
266 {
267 NTSTATUS Status;
268 HANDLE PinHandle;
269 PFILE_OBJECT FileObject;
270
271 Status = KsCreatePin(KMixerHandle, PinConnect, GENERIC_READ | GENERIC_WRITE, &PinHandle);
272
273 if (!NT_SUCCESS(Status))
274 {
275 DPRINT1("Failed to create Mixer Pin with %x\n", Status);
276 return STATUS_UNSUCCESSFUL;
277 }
278
279 Status = ObReferenceObjectByHandle(PinHandle,
280 GENERIC_READ | GENERIC_WRITE,
281 IoFileObjectType, KernelMode, (PVOID*)&FileObject, NULL);
282
283 if (!NT_SUCCESS(Status))
284 {
285 DPRINT1("Failed to get file object with %x\n", Status);
286 return STATUS_UNSUCCESSFUL;
287 }
288
289 Status = SetMixerInputOutputFormat(FileObject, InputFormat, OutputFormat);
290 if (!NT_SUCCESS(Status))
291 {
292 ObDereferenceObject(FileObject);
293 ZwClose(PinHandle);
294 }
295
296 ObDereferenceObject(FileObject);
297
298 *MixerPinHandle = PinHandle;
299 return Status;
300 }
301
302
303 NTSTATUS
304 NTAPI
305 InstantiatePins(
306 IN PKSAUDIO_DEVICE_ENTRY DeviceEntry,
307 IN PKSPIN_CONNECT Connect,
308 IN PDISPATCH_CONTEXT DispatchContext,
309 IN PSYSAUDIODEVEXT DeviceExtension)
310 {
311 NTSTATUS Status;
312 HANDLE RealPinHandle;
313 PKSDATAFORMAT_WAVEFORMATEX InputFormat;
314 PKSDATAFORMAT_WAVEFORMATEX OutputFormat = NULL;
315 PKSPIN_CONNECT MixerPinConnect = NULL;
316 KSPIN_CINSTANCES PinInstances;
317
318 DPRINT("InstantiatePins entered\n");
319
320 /* query instance count */
321 Status = GetPinInstanceCount(DeviceEntry, &PinInstances, Connect);
322 if (!NT_SUCCESS(Status))
323 {
324 /* failed to query instance count */
325 return Status;
326 }
327
328 /* can be the pin be instantiated */
329 if (PinInstances.PossibleCount == 0)
330 {
331 /* caller wanted to open an instance-less pin */
332 return STATUS_UNSUCCESSFUL;
333 }
334
335 /* has the maximum instance count been exceeded */
336 if (PinInstances.CurrentCount == PinInstances.PossibleCount)
337 {
338 /* FIXME pin already exists
339 * and kmixer infrastructure is not implemented
340 */
341 return STATUS_UNSUCCESSFUL;
342 }
343
344 /* Fetch input format */
345 InputFormat = (PKSDATAFORMAT_WAVEFORMATEX)(Connect + 1);
346
347 /* Let's try to create the audio irp pin */
348 Status = KsCreatePin(DeviceEntry->Handle, Connect, GENERIC_READ | GENERIC_WRITE, &RealPinHandle);
349
350 if (!NT_SUCCESS(Status))
351 {
352 /* the audio irp pin didnt accept the input format
353 * let's compute a compatible format
354 */
355 MixerPinConnect = ExAllocatePool(NonPagedPool, sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX));
356 if (!MixerPinConnect)
357 {
358 /* not enough memory */
359 return STATUS_INSUFFICIENT_RESOURCES;
360 }
361
362 /* Zero pin connect */
363 RtlZeroMemory(MixerPinConnect, sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX));
364
365 /* Copy initial connect details */
366 RtlMoveMemory(MixerPinConnect, Connect, sizeof(KSPIN_CONNECT));
367
368
369 OutputFormat = (PKSDATAFORMAT_WAVEFORMATEX)(MixerPinConnect + 1);
370
371 Status = ComputeCompatibleFormat(DeviceEntry, Connect->PinId, InputFormat, OutputFormat);
372 if (!NT_SUCCESS(Status))
373 {
374 DPRINT1("ComputeCompatibleFormat failed with %x\n", Status);
375 ExFreePool(MixerPinConnect);
376 return Status;
377 }
378
379 /* Retry with Mixer format */
380 Status = KsCreatePin(DeviceEntry->Handle, MixerPinConnect, GENERIC_READ | GENERIC_WRITE, &RealPinHandle);
381 if (!NT_SUCCESS(Status))
382 {
383 /* This should not fail */
384 DPRINT1("KsCreatePin failed with %x\n", Status);
385 DPRINT1(" InputFormat: SampleRate %u Bits %u Channels %u\n", InputFormat->WaveFormatEx.nSamplesPerSec, InputFormat->WaveFormatEx.wBitsPerSample, InputFormat->WaveFormatEx.nChannels);
386 DPRINT1("OutputFormat: SampleRate %u Bits %u Channels %u\n", OutputFormat->WaveFormatEx.nSamplesPerSec, OutputFormat->WaveFormatEx.wBitsPerSample, OutputFormat->WaveFormatEx.nChannels);
387
388 ExFreePool(MixerPinConnect);
389 return Status;
390 }
391 }
392
393 DeviceEntry->Pins[Connect->PinId].References = 0;
394
395 /* initialize dispatch context */
396 DispatchContext->Handle = RealPinHandle;
397 DispatchContext->PinId = Connect->PinId;
398 DispatchContext->AudioEntry = DeviceEntry;
399
400
401 /* Do we need to transform the audio stream */
402 if (OutputFormat != NULL)
403 {
404 /* Now create the mixer pin */
405 Status = CreateMixerPinAndSetFormat(DeviceExtension->KMixerHandle,
406 MixerPinConnect,
407 (PKSDATAFORMAT)InputFormat,
408 (PKSDATAFORMAT)OutputFormat,
409 &DispatchContext->hMixerPin);
410
411 /* check for success */
412 if (!NT_SUCCESS(Status))
413 {
414 DPRINT1("Failed to create Mixer Pin with %x\n", Status);
415 ExFreePool(MixerPinConnect);
416 }
417 }
418 /* done */
419 return Status;
420 }
421
422 NTSTATUS
423 NTAPI
424 DispatchCreateSysAudioPin(
425 IN PDEVICE_OBJECT DeviceObject,
426 IN PIRP Irp)
427 {
428 NTSTATUS Status = STATUS_SUCCESS;
429 PIO_STACK_LOCATION IoStack;
430 PKSAUDIO_DEVICE_ENTRY DeviceEntry;
431 PKSPIN_CONNECT Connect = NULL;
432 PDISPATCH_CONTEXT DispatchContext;
433
434 DPRINT("DispatchCreateSysAudioPin entered\n");
435
436 /* get current stack location */
437 IoStack = IoGetCurrentIrpStackLocation(Irp);
438
439 /* sanity checks */
440 ASSERT(IoStack->FileObject);
441 ASSERT(IoStack->FileObject->RelatedFileObject);
442 ASSERT(IoStack->FileObject->RelatedFileObject->FsContext);
443
444 /* get current attached virtual device */
445 DeviceEntry = (PKSAUDIO_DEVICE_ENTRY)IoStack->FileObject->RelatedFileObject->FsContext;
446
447 /* now validate pin connect request */
448 Status = KsValidateConnectRequest(Irp, DeviceEntry->PinDescriptorsCount, DeviceEntry->PinDescriptors, &Connect);
449
450 /* check for success */
451 if (!NT_SUCCESS(Status))
452 {
453 /* failed */
454 Irp->IoStatus.Status = Status;
455 IoCompleteRequest(Irp, IO_NO_INCREMENT);
456 return Status;
457 }
458
459 /* allocate dispatch context */
460 DispatchContext = ExAllocatePool(NonPagedPool, sizeof(DISPATCH_CONTEXT));
461 if (!DispatchContext)
462 {
463 /* failed */
464 Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
465 IoCompleteRequest(Irp, IO_NO_INCREMENT);
466 return STATUS_INSUFFICIENT_RESOURCES;
467 }
468
469 /* zero dispatch context */
470 RtlZeroMemory(DispatchContext, sizeof(DISPATCH_CONTEXT));
471
472 /* allocate object header */
473 Status = KsAllocateObjectHeader(&DispatchContext->ObjectHeader, 0, NULL, Irp, &PinTable);
474 if (!NT_SUCCESS(Status))
475 {
476 /* failed */
477 ExFreePool(DispatchContext);
478 Irp->IoStatus.Status = Status;
479 IoCompleteRequest(Irp, IO_NO_INCREMENT);
480 return Status;
481 }
482
483 /* now instantiate the pins */
484 Status = InstantiatePins(DeviceEntry, Connect, DispatchContext, (PSYSAUDIODEVEXT)DeviceObject->DeviceExtension);
485 if (!NT_SUCCESS(Status))
486 {
487 /* failed */
488 KsFreeObjectHeader(DispatchContext->ObjectHeader);
489 ExFreePool(DispatchContext);
490 }
491 else
492 {
493 /* store dispatch context */
494 IoStack->FileObject->FsContext = (PVOID)DispatchContext;
495 }
496
497
498 /* FIXME create items for clocks / allocators */
499 Irp->IoStatus.Status = Status;
500 IoCompleteRequest(Irp, IO_NO_INCREMENT);
501 return Status;
502 }