- Check that all pins have been closed when the last filter reference is gone
[reactos.git] / reactos / drivers / wdm / audio / backpln / portcls / irpstream.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: drivers/wdm/audio/backpln/portcls/irpstream.c
5 * PURPOSE: IRP Stream handling
6 * PROGRAMMER: Johannes Anderwald
7 */
8
9 #include "private.h"
10
11
12 typedef struct
13 {
14 IIrpQueueVtbl *lpVtbl;
15
16 LONG ref;
17
18 ULONG CurrentOffset;
19 LONG NumMappings;
20 ULONG NumDataAvailable;
21 BOOL StartStream;
22 KSPIN_CONNECT *ConnectDetails;
23 PKSDATAFORMAT_WAVEFORMATEX DataFormat;
24
25 KSPIN_LOCK IrpListLock;
26 LIST_ENTRY IrpList;
27 LIST_ENTRY FreeIrpList;
28 PIRP Irp;
29 PVOID SilenceBuffer;
30
31 ULONG OutOfMapping;
32 ULONG MaxFrameSize;
33 ULONG Alignment;
34 ULONG MinimumDataThreshold;
35
36 }IIrpQueueImpl;
37
38 NTSTATUS
39 NTAPI
40 IIrpQueue_fnQueryInterface(
41 IIrpQueue* iface,
42 IN REFIID refiid,
43 OUT PVOID* Output)
44 {
45 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
46
47 if (IsEqualGUIDAligned(refiid, &IID_IUnknown))
48 {
49 *Output = &This->lpVtbl;
50 _InterlockedIncrement(&This->ref);
51 return STATUS_SUCCESS;
52 }
53 return STATUS_UNSUCCESSFUL;
54 }
55
56 ULONG
57 NTAPI
58 IIrpQueue_fnAddRef(
59 IIrpQueue* iface)
60 {
61 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
62
63 return _InterlockedIncrement(&This->ref);
64 }
65
66 ULONG
67 NTAPI
68 IIrpQueue_fnRelease(
69 IIrpQueue* iface)
70 {
71 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
72
73 _InterlockedDecrement(&This->ref);
74
75 if (This->ref == 0)
76 {
77 FreeItem(This, TAG_PORTCLASS);
78 return 0;
79 }
80 /* Return new reference count */
81 return This->ref;
82 }
83
84
85 NTSTATUS
86 NTAPI
87 IIrpQueue_fnInit(
88 IN IIrpQueue *iface,
89 IN KSPIN_CONNECT *ConnectDetails,
90 IN PKSDATAFORMAT DataFormat,
91 IN PDEVICE_OBJECT DeviceObject,
92 IN ULONG FrameSize,
93 IN ULONG Alignment,
94 IN PVOID SilenceBuffer)
95 {
96 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
97
98 This->ConnectDetails = ConnectDetails;
99 This->DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)DataFormat;
100 This->MaxFrameSize = FrameSize;
101 This->SilenceBuffer = SilenceBuffer;
102 This->Alignment = Alignment;
103 This->MinimumDataThreshold = ((PKSDATAFORMAT_WAVEFORMATEX)DataFormat)->WaveFormatEx.nAvgBytesPerSec / 3;
104
105 InitializeListHead(&This->IrpList);
106 InitializeListHead(&This->FreeIrpList);
107 KeInitializeSpinLock(&This->IrpListLock);
108
109 return STATUS_SUCCESS;
110 }
111
112 NTSTATUS
113 NTAPI
114 IIrpQueue_fnAddMapping(
115 IN IIrpQueue *iface,
116 IN PUCHAR Buffer,
117 IN ULONG BufferSize,
118 IN PIRP Irp)
119 {
120 PKSSTREAM_HEADER Header;
121 NTSTATUS Status = STATUS_SUCCESS;
122 PIO_STACK_LOCATION IoStack;
123 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
124
125 /* get current irp stack location */
126 IoStack = IoGetCurrentIrpStackLocation(Irp);
127
128 if (!Buffer)
129 {
130 /* probe the stream irp */
131 Status = KsProbeStreamIrp(Irp, KSSTREAM_WRITE | KSPROBE_ALLOCATEMDL | KSPROBE_PROBEANDLOCK | KSPROBE_ALLOWFORMATCHANGE | KSPROBE_SYSTEMADDRESS, 0);
132
133 /* check for success */
134 if (!NT_SUCCESS(Status))
135 {
136 DPRINT1("KsProbeStreamIrp failed with %x\n", Status);
137 return Status;
138 }
139
140 /* get the stream header */
141 Header = (PKSSTREAM_HEADER)Irp->AssociatedIrp.SystemBuffer;
142 ASSERT(Header);
143 ASSERT(Irp->MdlAddress);
144
145 if (Irp->RequestorMode != KernelMode)
146 {
147 /* use allocated mdl */
148 Header->Data = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
149 }
150 }
151 else
152 {
153 /* HACK */
154 Header = (PKSSTREAM_HEADER)Buffer;
155 }
156
157 /* HACK */
158 Irp->Tail.Overlay.DriverContext[2] = (PVOID)Header;
159
160 /* sanity check */
161 ASSERT(Header);
162
163 /* dont exceed max frame size */
164 //ASSERT(This->MaxFrameSize >= Header->DataUsed);
165
166 /* increment num mappings */
167 InterlockedIncrement(&This->NumMappings);
168
169 /* increment num data available */
170 This->NumDataAvailable += Header->DataUsed;
171
172 /* mark irp as pending */
173 IoMarkIrpPending(Irp);
174
175 /* add irp to cancelable queue */
176 KsAddIrpToCancelableQueue(&This->IrpList, &This->IrpListLock, Irp, KsListEntryTail, NULL);
177
178 /* done */
179 return Status;
180 }
181
182 NTSTATUS
183 NTAPI
184 IIrpQueue_fnGetMapping(
185 IN IIrpQueue *iface,
186 OUT PUCHAR * Buffer,
187 OUT PULONG BufferSize)
188 {
189 PIRP Irp;
190 ULONG Offset;
191 //PIO_STACK_LOCATION IoStack;
192 PKSSTREAM_HEADER StreamHeader;
193 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
194
195 /* check if there is an irp in the partially processed */
196 if (This->Irp)
197 {
198 /* use last irp */
199 if (This->Irp->Cancel == FALSE)
200 {
201 Irp = This->Irp;
202 Offset = This->CurrentOffset;
203 }
204 else
205 {
206 /* irp has been cancelled */
207 This->Irp->IoStatus.Status = STATUS_CANCELLED;
208 IoCompleteRequest(This->Irp, IO_NO_INCREMENT);
209 This->Irp = Irp = NULL;
210 }
211 }
212 else
213 {
214 /* get a fresh new irp from the queue */
215 This->Irp = Irp = KsRemoveIrpFromCancelableQueue(&This->IrpList, &This->IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
216 This->CurrentOffset = Offset = 0;
217 }
218
219 if (!Irp)
220 {
221 /* no irp available, use silence buffer */
222 *Buffer = This->SilenceBuffer;
223 *BufferSize = This->MaxFrameSize;
224 /* flag for port wave pci driver */
225 This->OutOfMapping = TRUE;
226 /* indicate flag to restart fast buffering */
227 This->StartStream = FALSE;
228 return STATUS_SUCCESS;
229 }
230
231 #if 0
232 /* get current irp stack location */
233 IoStack = IoGetCurrentIrpStackLocation(Irp);
234
235 /* get stream header */
236 StreamHeader = (PKSSTREAM_HEADER)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
237 #else
238 /* HACK get stream header */
239 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
240 #endif
241
242 /* sanity check */
243 ASSERT(StreamHeader);
244
245 /* store buffersize */
246 *BufferSize = StreamHeader->DataUsed - Offset;
247
248 /* store buffer */
249 *Buffer = &((PUCHAR)StreamHeader->Data)[Offset];
250
251 /* unset flag that no irps are available */
252 This->OutOfMapping = FALSE;
253
254 return STATUS_SUCCESS;
255 }
256
257 VOID
258 NTAPI
259 IIrpQueue_fnUpdateMapping(
260 IN IIrpQueue *iface,
261 IN ULONG BytesWritten)
262 {
263 //PIO_STACK_LOCATION IoStack;
264 PKSSTREAM_HEADER StreamHeader;
265 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
266
267 if (!This->Irp)
268 {
269 /* silence buffer was used */
270 return;
271 }
272
273 #if 0
274 /* get current irp stack location */
275 IoStack = IoGetCurrentIrpStackLocation(This->Irp);
276
277 /* get stream header */
278 StreamHeader = (PKSSTREAM_HEADER)IoStack->Parameters.DeviceIoControl.Type3InputBuffer;
279 #else
280 /* HACK get stream header */
281 StreamHeader = (PKSSTREAM_HEADER)This->Irp->Tail.Overlay.DriverContext[2];
282 #endif
283
284 /* sanity check */
285 ASSERT(StreamHeader);
286
287 /* add to current offset */
288 This->CurrentOffset += BytesWritten;
289
290 /* decrement available data counter */
291 This->NumDataAvailable -= BytesWritten;
292
293 if (This->CurrentOffset >= StreamHeader->DataUsed)
294 {
295 /* irp has been processed completly */
296 This->Irp->IoStatus.Status = STATUS_SUCCESS;
297
298 /* frame extend contains the original request size, DataUsed contains the real buffer size
299 * is different when kmixer performs channel conversion, upsampling etc
300 */
301 This->Irp->IoStatus.Information = StreamHeader->FrameExtent;
302
303 if (This->Irp->RequestorMode != KernelMode)
304 {
305 /* HACK - WDMAUD should pass PKSSTREAM_HEADERs */
306 ExFreePool(StreamHeader->Data);
307 ExFreePool(StreamHeader);
308 }
309
310 /* complete the request */
311 IoCompleteRequest(This->Irp, IO_SOUND_INCREMENT);
312 /* remove irp as it is complete */
313 This->Irp = NULL;
314 This->CurrentOffset = 0;
315 }
316 }
317
318 ULONG
319 NTAPI
320 IIrpQueue_fnNumMappings(
321 IN IIrpQueue *iface)
322 {
323 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
324
325 /* returns the amount of mappings available */
326 return This->NumMappings;
327 }
328
329 ULONG
330 NTAPI
331 IIrpQueue_fnNumData(
332 IN IIrpQueue *iface)
333 {
334 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
335 /* returns the amount of audio stream data available */
336 return This->NumDataAvailable;
337 }
338
339
340 BOOL
341 NTAPI
342 IIrpQueue_fnMinimumDataAvailable(
343 IN IIrpQueue *iface)
344 {
345 BOOL Result;
346 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
347
348 if (This->StartStream)
349 return TRUE;
350
351 if (This->MinimumDataThreshold < This->NumDataAvailable)
352 {
353 This->StartStream = TRUE;
354 Result = TRUE;
355 }
356 else
357 {
358 Result = FALSE;
359 }
360 return Result;
361 }
362
363 BOOL
364 NTAPI
365 IIrpQueue_fnCancelBuffers(
366 IN IIrpQueue *iface)
367 {
368 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
369
370 This->StartStream = FALSE;
371 return TRUE;
372 }
373
374 VOID
375 NTAPI
376 IIrpQueue_fnUpdateFormat(
377 IN IIrpQueue *iface,
378 PKSDATAFORMAT DataFormat)
379 {
380 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
381 This->DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)DataFormat;
382 This->MinimumDataThreshold = This->DataFormat->WaveFormatEx.nAvgBytesPerSec / 3;
383 This->StartStream = FALSE;
384 This->NumDataAvailable = 0;
385 }
386
387 NTSTATUS
388 NTAPI
389 IIrpQueue_fnGetMappingWithTag(
390 IN IIrpQueue *iface,
391 IN PVOID Tag,
392 OUT PPHYSICAL_ADDRESS PhysicalAddress,
393 OUT PVOID *VirtualAddress,
394 OUT PULONG ByteCount,
395 OUT PULONG Flags)
396 {
397 PKSSTREAM_HEADER StreamHeader;
398 PIRP Irp;
399 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
400
401 *Flags = 0;
402 ASSERT(Tag != NULL);
403
404 /* get an irp from the queue */
405 Irp = KsRemoveIrpFromCancelableQueue(&This->IrpList, &This->IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
406
407 /* check if there is an irp */
408 if (!Irp)
409 {
410 /* no irp available */
411 This->OutOfMapping = TRUE;
412 This->StartStream = FALSE;
413 return STATUS_UNSUCCESSFUL;
414 }
415
416 /* HACK get stream header */
417 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
418
419 /* store mapping in the free list */
420 ExInterlockedInsertTailList(&This->FreeIrpList, &Irp->Tail.Overlay.ListEntry, &This->IrpListLock);
421
422 /* return mapping */
423 *PhysicalAddress = MmGetPhysicalAddress(StreamHeader->Data);
424 *VirtualAddress = StreamHeader->Data;
425 *ByteCount = StreamHeader->DataUsed;
426
427 /* decrement mapping count */
428 InterlockedDecrement(&This->NumMappings);
429 /* decrement num data available */
430 This->NumDataAvailable -= StreamHeader->DataUsed;
431
432 /* store tag in irp */
433 Irp->Tail.Overlay.DriverContext[3] = Tag;
434
435 /* done */
436 return STATUS_SUCCESS;
437 }
438
439 NTSTATUS
440 NTAPI
441 IIrpQueue_fnReleaseMappingWithTag(
442 IN IIrpQueue *iface,
443 IN PVOID Tag)
444 {
445 PIRP Irp;
446 PLIST_ENTRY CurEntry;
447 PKSSTREAM_HEADER StreamHeader;
448 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
449
450 DPRINT("IIrpQueue_fnReleaseMappingWithTag Tag %p\n", Tag);
451
452 /* remove irp from used list */
453 CurEntry = ExInterlockedRemoveHeadList(&This->FreeIrpList, &This->IrpListLock);
454 /* sanity check */
455 ASSERT(CurEntry);
456
457 /* get irp from list entry */
458 Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
459
460 /* HACK get stream header */
461 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
462
463 /* driver must release items in the same order */
464 ASSERT(Irp->Tail.Overlay.DriverContext[3] == Tag);
465
466 /* irp has been processed completly */
467 Irp->IoStatus.Status = STATUS_SUCCESS;
468
469 /* frame extend contains the original request size, DataUsed contains the real buffer size
470 * is different when kmixer performs channel conversion, upsampling etc
471 */
472 Irp->IoStatus.Information = StreamHeader->FrameExtent;
473
474 /* free stream data, no tag as wdmaud.drv does it atm */
475 ExFreePool(StreamHeader->Data);
476
477 /* free stream header, no tag as wdmaud.drv allocates it atm */
478 ExFreePool(StreamHeader);
479
480 /* complete the request */
481 IoCompleteRequest(Irp, IO_SOUND_INCREMENT);
482
483 return STATUS_SUCCESS;
484 }
485
486 BOOL
487 NTAPI
488 IIrpQueue_fnHasLastMappingFailed(
489 IN IIrpQueue *iface)
490 {
491 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
492 return This->OutOfMapping;
493 }
494
495 VOID
496 NTAPI
497 IIrpQueue_fnPrintQueueStatus(
498 IN IIrpQueue *iface)
499 {
500
501 }
502
503 VOID
504 NTAPI
505 IIrpQueue_fnSetMinimumDataThreshold(
506 IN IIrpQueue *iface,
507 ULONG MinimumDataThreshold)
508 {
509 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
510
511 This->MinimumDataThreshold = MinimumDataThreshold;
512 }
513
514 ULONG
515 NTAPI
516 IIrpQueue_fnGetMinimumDataThreshold(
517 IN IIrpQueue *iface)
518 {
519 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
520
521 return This->MinimumDataThreshold;
522 }
523
524
525 static IIrpQueueVtbl vt_IIrpQueue =
526 {
527 IIrpQueue_fnQueryInterface,
528 IIrpQueue_fnAddRef,
529 IIrpQueue_fnRelease,
530 IIrpQueue_fnInit,
531 IIrpQueue_fnAddMapping,
532 IIrpQueue_fnGetMapping,
533 IIrpQueue_fnUpdateMapping,
534 IIrpQueue_fnNumMappings,
535 IIrpQueue_fnNumData,
536 IIrpQueue_fnMinimumDataAvailable,
537 IIrpQueue_fnCancelBuffers,
538 IIrpQueue_fnUpdateFormat,
539 IIrpQueue_fnGetMappingWithTag,
540 IIrpQueue_fnReleaseMappingWithTag,
541 IIrpQueue_fnHasLastMappingFailed,
542 IIrpQueue_fnPrintQueueStatus,
543 IIrpQueue_fnSetMinimumDataThreshold,
544 IIrpQueue_fnGetMinimumDataThreshold
545 };
546
547 NTSTATUS
548 NTAPI
549 NewIrpQueue(
550 IN IIrpQueue **Queue)
551 {
552 IIrpQueueImpl *This = AllocateItem(NonPagedPool, sizeof(IIrpQueueImpl), TAG_PORTCLASS);
553 if (!This)
554 return STATUS_INSUFFICIENT_RESOURCES;
555
556 This->ref = 1;
557 This->lpVtbl = &vt_IIrpQueue;
558
559 *Queue = (IIrpQueue*)This;
560 return STATUS_SUCCESS;
561
562 }
563