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