sync with trunk r47346
[reactos.git] / 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 = 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 ExFreePool(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 = ExAllocatePool(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 ExFreePool(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 ExFreePool(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 ExFreePool(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) + 2) * 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 = ExAllocatePool(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 = ExAllocatePool(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 ExFreePool(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 ExFreePool(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 }