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