[KS]
[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 /* free stream data, no tag as wdmaud.drv does it atm */
304 //ExFreePool(StreamHeader->Data);
305
306 /* free stream header, no tag as wdmaud.drv allocates it atm */
307 //ExFreePool(StreamHeader);
308
309 /* complete the request */
310 IoCompleteRequest(This->Irp, IO_SOUND_INCREMENT);
311 /* remove irp as it is complete */
312 This->Irp = NULL;
313 This->CurrentOffset = 0;
314 }
315 }
316
317 ULONG
318 NTAPI
319 IIrpQueue_fnNumMappings(
320 IN IIrpQueue *iface)
321 {
322 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
323
324 /* returns the amount of mappings available */
325 return This->NumMappings;
326 }
327
328 ULONG
329 NTAPI
330 IIrpQueue_fnNumData(
331 IN IIrpQueue *iface)
332 {
333 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
334 /* returns the amount of audio stream data available */
335 return This->NumDataAvailable;
336 }
337
338
339 BOOL
340 NTAPI
341 IIrpQueue_fnMinimumDataAvailable(
342 IN IIrpQueue *iface)
343 {
344 BOOL Result;
345 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
346
347 if (This->StartStream)
348 return TRUE;
349
350 if (This->MinimumDataThreshold < This->NumDataAvailable)
351 {
352 This->StartStream = TRUE;
353 Result = TRUE;
354 }
355 else
356 {
357 Result = FALSE;
358 }
359 return Result;
360 }
361
362 BOOL
363 NTAPI
364 IIrpQueue_fnCancelBuffers(
365 IN IIrpQueue *iface)
366 {
367 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
368
369 This->StartStream = FALSE;
370 return TRUE;
371 }
372
373 VOID
374 NTAPI
375 IIrpQueue_fnUpdateFormat(
376 IN IIrpQueue *iface,
377 PKSDATAFORMAT DataFormat)
378 {
379 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
380 This->DataFormat = (PKSDATAFORMAT_WAVEFORMATEX)DataFormat;
381 This->MinimumDataThreshold = This->DataFormat->WaveFormatEx.nAvgBytesPerSec / 3;
382 This->StartStream = FALSE;
383 This->NumDataAvailable = 0;
384 }
385
386 NTSTATUS
387 NTAPI
388 IIrpQueue_fnGetMappingWithTag(
389 IN IIrpQueue *iface,
390 IN PVOID Tag,
391 OUT PPHYSICAL_ADDRESS PhysicalAddress,
392 OUT PVOID *VirtualAddress,
393 OUT PULONG ByteCount,
394 OUT PULONG Flags)
395 {
396 PKSSTREAM_HEADER StreamHeader;
397 PIRP Irp;
398 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
399
400 *Flags = 0;
401 ASSERT(Tag != NULL);
402
403 /* get an irp from the queue */
404 Irp = KsRemoveIrpFromCancelableQueue(&This->IrpList, &This->IrpListLock, KsListEntryHead, KsAcquireAndRemoveOnlySingleItem);
405
406 /* check if there is an irp */
407 if (!Irp)
408 {
409 /* no irp available */
410 This->OutOfMapping = TRUE;
411 This->StartStream = FALSE;
412 return STATUS_UNSUCCESSFUL;
413 }
414
415 /* HACK get stream header */
416 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
417
418 /* store mapping in the free list */
419 ExInterlockedInsertTailList(&This->FreeIrpList, &Irp->Tail.Overlay.ListEntry, &This->IrpListLock);
420
421 /* return mapping */
422 *PhysicalAddress = MmGetPhysicalAddress(StreamHeader->Data);
423 *VirtualAddress = StreamHeader->Data;
424 *ByteCount = StreamHeader->DataUsed;
425
426 /* decrement mapping count */
427 InterlockedDecrement(&This->NumMappings);
428 /* decrement num data available */
429 This->NumDataAvailable -= StreamHeader->DataUsed;
430
431 /* store tag in irp */
432 Irp->Tail.Overlay.DriverContext[3] = Tag;
433
434 /* done */
435 return STATUS_SUCCESS;
436 }
437
438 NTSTATUS
439 NTAPI
440 IIrpQueue_fnReleaseMappingWithTag(
441 IN IIrpQueue *iface,
442 IN PVOID Tag)
443 {
444 PIRP Irp;
445 PLIST_ENTRY CurEntry;
446 PKSSTREAM_HEADER StreamHeader;
447 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
448
449 DPRINT("IIrpQueue_fnReleaseMappingWithTag Tag %p\n", Tag);
450
451 /* remove irp from used list */
452 CurEntry = ExInterlockedRemoveHeadList(&This->FreeIrpList, &This->IrpListLock);
453 /* sanity check */
454 ASSERT(CurEntry);
455
456 /* get irp from list entry */
457 Irp = (PIRP)CONTAINING_RECORD(CurEntry, IRP, Tail.Overlay.ListEntry);
458
459 /* HACK get stream header */
460 StreamHeader = (PKSSTREAM_HEADER)Irp->Tail.Overlay.DriverContext[2];
461
462 /* driver must release items in the same order */
463 ASSERT(Irp->Tail.Overlay.DriverContext[3] == Tag);
464
465 /* irp has been processed completly */
466 Irp->IoStatus.Status = STATUS_SUCCESS;
467
468 /* frame extend contains the original request size, DataUsed contains the real buffer size
469 * is different when kmixer performs channel conversion, upsampling etc
470 */
471 Irp->IoStatus.Information = StreamHeader->FrameExtent;
472
473 /* free stream data, no tag as wdmaud.drv does it atm */
474 ExFreePool(StreamHeader->Data);
475
476 /* free stream header, no tag as wdmaud.drv allocates it atm */
477 ExFreePool(StreamHeader);
478
479 /* complete the request */
480 IoCompleteRequest(Irp, IO_SOUND_INCREMENT);
481
482 return STATUS_SUCCESS;
483 }
484
485 BOOL
486 NTAPI
487 IIrpQueue_fnHasLastMappingFailed(
488 IN IIrpQueue *iface)
489 {
490 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
491 return This->OutOfMapping;
492 }
493
494 VOID
495 NTAPI
496 IIrpQueue_fnPrintQueueStatus(
497 IN IIrpQueue *iface)
498 {
499
500 }
501
502 VOID
503 NTAPI
504 IIrpQueue_fnSetMinimumDataThreshold(
505 IN IIrpQueue *iface,
506 ULONG MinimumDataThreshold)
507 {
508 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
509
510 This->MinimumDataThreshold = MinimumDataThreshold;
511 }
512
513 ULONG
514 NTAPI
515 IIrpQueue_fnGetMinimumDataThreshold(
516 IN IIrpQueue *iface)
517 {
518 IIrpQueueImpl * This = (IIrpQueueImpl*)iface;
519
520 return This->MinimumDataThreshold;
521 }
522
523
524 static IIrpQueueVtbl vt_IIrpQueue =
525 {
526 IIrpQueue_fnQueryInterface,
527 IIrpQueue_fnAddRef,
528 IIrpQueue_fnRelease,
529 IIrpQueue_fnInit,
530 IIrpQueue_fnAddMapping,
531 IIrpQueue_fnGetMapping,
532 IIrpQueue_fnUpdateMapping,
533 IIrpQueue_fnNumMappings,
534 IIrpQueue_fnNumData,
535 IIrpQueue_fnMinimumDataAvailable,
536 IIrpQueue_fnCancelBuffers,
537 IIrpQueue_fnUpdateFormat,
538 IIrpQueue_fnGetMappingWithTag,
539 IIrpQueue_fnReleaseMappingWithTag,
540 IIrpQueue_fnHasLastMappingFailed,
541 IIrpQueue_fnPrintQueueStatus,
542 IIrpQueue_fnSetMinimumDataThreshold,
543 IIrpQueue_fnGetMinimumDataThreshold
544 };
545
546 NTSTATUS
547 NTAPI
548 NewIrpQueue(
549 IN IIrpQueue **Queue)
550 {
551 IIrpQueueImpl *This = AllocateItem(NonPagedPool, sizeof(IIrpQueueImpl), TAG_PORTCLASS);
552 if (!This)
553 return STATUS_INSUFFICIENT_RESOURCES;
554
555 This->ref = 1;
556 This->lpVtbl = &vt_IIrpQueue;
557
558 *Queue = (IIrpQueue*)This;
559 return STATUS_SUCCESS;
560
561 }
562